#include <sdk.h>
#include "app.h"
#include "batchbuild.h"
#include "M_Code_auibook.h"
#include "M_Code_StyledTextCtrl.h"
#include "compilersettingsdlg.h"
#include "debuggersettingsdlg.h"
#include "environmentsettingsdlg.h"
#include "infopane.h"
#include "infowindow.h"
#include "main.h"
#include "notebookstyles.h"
#include "scriptconsole.h"
#include "scriptingsettingsdlg.h"
#include "switcherdlg.h"
#include "M_Code_statusbar.h"
#include "loggers.h"
#include <wx/display.h>
#include <wx/dnd.h>
#include <wx/fileconf.h>
#include <wx/filename.h>
#include <wx/gdicmn.h>
#include <wx/printdlg.h>
#include <wx/sstream.h>
#include <wx/tipdlg.h>
#include <wx/tokenzr.h>
#include <wx/xrc/xmlres.h>
#include <M_Code_exception.h>
#include <M_Code_plugin.h>
#include <M_Code_project.h>
#include <M_Code_workspace.h>
#include <ccmanager.h>
#include <configmanager.h>
#include <debuggermanager.h>
#include <editorcolourset.h>
#include <editormanager.h>
#include <filefilters.h>
#include <globals.h>
#include <logmanager.h>
#include <personalitymanager.h>
#include <pluginmanager.h>
#include <projectmanager.h>
#include <scriptingmanager.h>
#include <sdk_events.h>
#include <templatemanager.h>
#include <toolsmanager.h>
#include <uservarmanager.h>
#include "debugger_interface_creator.h"
#include "debuggermenu.h"
#include "M_Code_colourmanager.h"
#include "editorconfigurationdlg.h"
#include "projectmanagerui.h"

class cbFileDropTarget : public wxFileDropTarget {
  public:
    cbFileDropTarget( MainFrame *frame ): m_frame( frame ) {}
    virtual bool OnDropFiles( wxCoord x, wxCoord y, const wxArrayString& filenames ) {
      if( !m_frame ) {
        return false;
      }
      return m_frame->OnDropFiles( x, y, filenames );
    }
  private:
    MainFrame* m_frame;
};

const static wxString gDefaultLayout = _T( "default" );
static wxString gDefaultLayoutData;
static wxString gDefaultMessagePaneLayoutData;
const static wxString gMinimalLayout = _T( "minimal" );
static wxString gMinimalLayoutData;
static wxString gMinimalMessagePaneLayoutData;

enum {
  wxID_CBFILE01 = 5060,   // 最近的文件
  wxID_CBFILE02,
  wxID_CBFILE03,
  wxID_CBFILE04,
  wxID_CBFILE05,
  wxID_CBFILE06,
  wxID_CBFILE07,
  wxID_CBFILE08,
  wxID_CBFILE09,
  wxID_CBFILE10,
  wxID_CBFILE11,
  wxID_CBFILE12,
  wxID_CBFILE13,
  wxID_CBFILE14,
  wxID_CBFILE15,
  wxID_CBFILE16,
  wxID_CBFILE17 = 5076,  // 最近的项目
  wxID_CBFILE18,
  wxID_CBFILE19,
  wxID_CBFILE20,
  wxID_CBFILE21,
  wxID_CBFILE22,
  wxID_CBFILE23,
  wxID_CBFILE24,
  wxID_CBFILE25,
  wxID_CBFILE26,
  wxID_CBFILE27,
  wxID_CBFILE28,
  wxID_CBFILE29,
  wxID_CBFILE30,
  wxID_CBFILE31,
  wxID_CBFILE32
};

int idToolNew = XRCID( "idToolNew" );
int idFileNew = XRCID( "idFileNew" );
int idFileNewEmpty = XRCID( "idFileNewEmpty" );
int idFileNewProject = XRCID( "idFileNewProject" );
int idFileNewTarget = XRCID( "idFileNewTarget" );
int idFileNewFile = XRCID( "idFileNewFile" );
int idFileNewCustom = XRCID( "idFileNewCustom" );
int idFileNewUser = XRCID( "idFileNewUser" );
int idFileOpen = XRCID( "idFileOpen" );
int idFileReopen = XRCID( "idFileReopen" );
int idFileReopenProject = XRCID( "idFileReopenProject" );
int idFileOpenRecentFileClearHistory = XRCID( "idFileOpenRecentFileClearHistory" );
int idFileOpenRecentProjectClearHistory = XRCID( "idFileOpenRecentProjectClearHistory" );
int idFileSave = XRCID( "idFileSave" );
int idFileSaveAs = XRCID( "idFileSaveAs" );
int idFileSaveProject = XRCID( "idFileSaveProject" );
int idFileSaveProjectAs = XRCID( "idFileSaveProjectAs" );
int idFileSaveProjectTemplate = XRCID( "idFileSaveProjectTemplate" );
int idFileOpenDefWorkspace = XRCID( "idFileOpenDefWorkspace" );
int idFileSaveWorkspace = XRCID( "idFileSaveWorkspace" );
int idFileSaveWorkspaceAs = XRCID( "idFileSaveWorkspaceAs" );
int idFileSaveAll = XRCID( "idFileSaveAll" );
int idFileCloseWorkspace = XRCID( "idFileCloseWorkspace" );
int idFileClose = XRCID( "idFileClose" );
int idFileCloseAll = XRCID( "idFileCloseAll" );
int idFileCloseProject = XRCID( "idFileCloseProject" );
int idFilePrintSetup = XRCID( "idFilePrintSetup" );
int idFileExit = XRCID( "idFileExit" );
int idEditUndo = XRCID( "idEditUndo" );
int idEditRedo = XRCID( "idEditRedo" );
int idEditClearHistory = XRCID( "idEditClearHistory" );
int idEditSwapHeaderSource = XRCID( "idEditSwapHeaderSource" );
int idEditGotoMatchingBrace = XRCID( "idEditGotoMatchingBrace" );
int idEditHighlightMode = XRCID( "idEditHighlightMode" );
int idEditHighlightModeText = XRCID( "idEditHighlightModeText" );
int idEditBookmarks = XRCID( "idEditBookmarks" );
int idEditBookmarksToggle = XRCID( "idEditBookmarksToggle" );
int idEditBookmarksPrevious = XRCID( "idEditBookmarksPrevious" );
int idEditBookmarksNext = XRCID( "idEditBookmarksNext" );
int idEditBookmarksClearAll = XRCID( "idEditBookmarksClearAll" );
int idEditFolding = XRCID( "idEditFolding" );
int idEditFoldAll = XRCID( "idEditFoldAll" );
int idEditUnfoldAll = XRCID( "idEditUnfoldAll" );
int idEditToggleAllFolds = XRCID( "idEditToggleAllFolds" );
int idEditFoldBlock = XRCID( "idEditFoldBlock" );
int idEditUnfoldBlock = XRCID( "idEditUnfoldBlock" );
int idEditToggleFoldBlock = XRCID( "idEditToggleFoldBlock" );
int idEditEncoding = XRCID( "idEditEncoding" );
int idEditEncodingDefault = XRCID( "idEditEncodingDefault" );
int idEditEncodingUseBom = XRCID( "idEditEncodingUseBom" );
int idEditEncodingAscii = XRCID( "idEditEncodingAscii" );
int idEditEncodingUtf7 = XRCID( "idEditEncodingUtf7" );
int idEditEncodingUtf8 = XRCID( "idEditEncodingUtf8" );
int idEditEncodingUnicode = XRCID( "idEditEncodingUnicode" );
int idEditEncodingUtf16 = XRCID( "idEditEncodingUtf16" );
int idEditEncodingUtf32 = XRCID( "idEditEncodingUtf32" );
int idEditEncodingUnicode16BE = XRCID( "idEditEncodingUnicode16BE" );
int idEditEncodingUnicode16LE = XRCID( "idEditEncodingUnicode16LE" );
int idEditEncodingUnicode32BE = XRCID( "idEditEncodingUnicode32BE" );
int idEditEncodingUnicode32LE = XRCID( "idEditEncodingUnicode32LE" );
int idEditSpecialCommands = XRCID( "idEditSpecialCommands" );
int idEditSpecialCommandsMovement = XRCID( "idEditSpecialCommandsMovement" );
int idEditParaUp = XRCID( "idEditParaUp" );
int idEditParaUpExtend = XRCID( "idEditParaUpExtend" );
int idEditParaDown = XRCID( "idEditParaDown" );
int idEditParaDownExtend = XRCID( "idEditParaDownExtend" );
int idEditWordPartLeft = XRCID( "idEditWordPartLeft" );
int idEditWordPartLeftExtend = XRCID( "idEditWordPartLeftExtend" );
int idEditWordPartRight = XRCID( "idEditWordPartRight" );
int idEditWordPartRightExtend = XRCID( "idEditWordPartRightExtend" );
int idEditSpecialCommandsZoom = XRCID( "idEditSpecialCommandsZoom" );
int idEditZoomIn = XRCID( "idEditZoomIn" );
int idEditZoomOut = XRCID( "idEditZoomOut" );
int idEditZoomReset = XRCID( "idEditZoomReset" );
int idEditSpecialCommandsLine = XRCID( "idEditSpecialCommandsLine" );
int idEditLineCut = XRCID( "idEditLineCut" );
int idEditLineDelete = XRCID( "idEditLineDelete" );
int idEditLineDuplicate = XRCID( "idEditLineDuplicate" );
int idEditLineTranspose = XRCID( "idEditLineTranspose" );
int idEditLineCopy = XRCID( "idEditLineCopy" );
int idEditLinePaste = XRCID( "idEditLinePaste" );
int idEditLineUp = XRCID( "idEditLineUp" );
int idEditLineDown = XRCID( "idEditLineDown" );
int idEditSpecialCommandsCase = XRCID( "idEditSpecialCommandsCase" );
int idEditUpperCase = XRCID( "idEditUpperCase" );
int idEditLowerCase = XRCID( "idEditLowerCase" );
int idEditSpecialCommandsOther = XRCID( "idEditSpecialCommandsOther" );
int idEditInsertNewLine = XRCID( "idEditInsertNewLine" );
int idEditGotoLineEnd = XRCID( "idEditGotoLineEnd" );
int idEditInsertNewLineBelow = XRCID( "idEditInsertNewLineBelow" );
int idEditInsertNewLineAbove = XRCID( "idEditInsertNewLineAbove" );
int idEditSelectAll = XRCID( "idEditSelectAll" );
int idEditSelectNext = XRCID( "idEditSelectNext" );
int idEditSelectNextSkip = XRCID( "idEditSelectNextSkip" );
int idEditCommentSelected = XRCID( "idEditCommentSelected" );
int idEditUncommentSelected = XRCID( "idEditUncommentSelected" );
int idEditToggleCommentSelected = XRCID( "idEditToggleCommentSelected" );
int idEditStreamCommentSelected = XRCID( "idEditStreamCommentSelected" );
int idEditBoxCommentSelected = XRCID( "idEditBoxCommentSelected" );
int idEditShowCallTip = XRCID( "idEditShowCallTip" );
int idEditCompleteCode = wxNewId();
int idViewLayoutDelete = XRCID( "idViewLayoutDelete" );
int idViewLayoutSave = XRCID( "idViewLayoutSave" );
int idViewToolbars = XRCID( "idViewToolbars" );
int idViewToolFit = XRCID( "idViewToolFit" );
int idViewToolOptimize = XRCID( "idViewToolOptimize" );
int idViewToolMain = XRCID( "idViewToolMain" );
int idViewToolDebugger = XRCID( "idViewToolDebugger" );
int idViewManager = XRCID( "idViewManager" );
int idViewLogManager = XRCID( "idViewLogManager" );
int idViewStatusbar = XRCID( "idViewStatusbar" );
int idViewScriptConsole = XRCID( "idViewScriptConsole" );
int idViewHideEditorTabs = XRCID( "idViewHideEditorTabs" );
int idViewFocusEditor = XRCID( "idViewFocusEditor" );
int idViewFocusManagement = XRCID( "idViewFocusManagement" );
int idViewFocusLogsAndOthers = XRCID( "idViewFocusLogsAndOthers" );
int idViewSwitchTabs = XRCID( "idViewSwitchTabs" );
int idViewFullScreen = XRCID( "idViewFullScreen" );
int idSearchFind = XRCID( "idSearchFind" );
int idSearchFindInFiles = XRCID( "idSearchFindInFiles" );
int idSearchFindNext = XRCID( "idSearchFindNext" );
int idSearchFindPrevious = XRCID( "idSearchFindPrevious" );
int idSearchFindSelectedNext = XRCID( "idSearchFindSelectedNext" );
int idSearchFindSelectedPrevious = XRCID( "idSearchFindSelectedPrevious" );
int idSearchReplace = XRCID( "idSearchReplace" );
int idSearchReplaceInFiles = XRCID( "idSearchReplaceInFiles" );
int idSearchGotoLine = XRCID( "idSearchGotoLine" );
int idSearchGotoNextChanged = XRCID( "idSearchGotoNextChanged" );
int idSearchGotoPreviousChanged = XRCID( "idSearchGotoPreviousChanged" );
int idSettingsEnvironment = XRCID( "idSettingsEnvironment" );
int idSettingsGlobalUserVars = XRCID( "idSettingsGlobalUserVars" );
int idSettingsEditor = XRCID( "idSettingsEditor" );
int idSettingsCompiler = XRCID( "idSettingsCompiler" );
int idSettingsDebugger = XRCID( "idSettingsDebugger" );
int idPluginsManagePlugins = XRCID( "idPluginsManagePlugins" );
int idSettingsScripting = XRCID( "idSettingsScripting" );
int idHelpTips = XRCID( "idHelpTips" );
int idHelpPlugins = XRCID( "idHelpPlugins" );
int idLeftSash = XRCID( "idLeftSash" );
int idBottomSash = XRCID( "idBottomSash" );
int idCloseFullScreen = XRCID( "idCloseFullScreen" );
int idFileNext = wxNewId();
int idFilePrev = wxNewId();
int idShiftTab = wxNewId();
int idCtrlAltTab = wxNewId();
int idStartHerePageLink = wxNewId();
int idHighlightButton = wxNewId();

BEGIN_EVENT_TABLE( MainFrame, wxFrame )
  EVT_ERASE_BACKGROUND( MainFrame::OnEraseBackground )
  EVT_SIZE( MainFrame::OnSize )
  EVT_CLOSE( MainFrame::OnApplicationClose )
  EVT_UPDATE_UI( idFileOpen, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileOpenRecentFileClearHistory, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileOpenRecentProjectClearHistory, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileSave, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileSaveAs, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileOpenDefWorkspace, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileSaveWorkspace, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileSaveWorkspaceAs, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileCloseWorkspace, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileClose, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileCloseAll, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFilePrintSetup, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileReopenProject, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileSaveProject, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileSaveProjectAs, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileSaveProjectTemplate, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileSaveAll, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idFileCloseProject, MainFrame::OnFileMenuUpdateUI )
  EVT_UPDATE_UI( idEditUndo, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditRedo, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditClearHistory, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditSwapHeaderSource, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditGotoMatchingBrace, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditFoldAll, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditUnfoldAll, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditToggleAllFolds, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditFoldBlock, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditUnfoldBlock, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditToggleFoldBlock, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditEncoding, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditSelectAll, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditSelectNext, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditSelectNextSkip, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditBookmarksToggle, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditBookmarksNext, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditBookmarksPrevious, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditBookmarksClearAll, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditCommentSelected, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditUncommentSelected, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditToggleCommentSelected, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditStreamCommentSelected, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditBoxCommentSelected, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditShowCallTip, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idEditCompleteCode, MainFrame::OnEditMenuUpdateUI )
  EVT_UPDATE_UI( idSearchFind, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchFindInFiles, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchFindNext, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchFindPrevious, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchFindSelectedNext, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchFindSelectedPrevious, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchReplace, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchReplaceInFiles, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchGotoLine, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchGotoNextChanged, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idSearchGotoPreviousChanged, MainFrame::OnSearchMenuUpdateUI )
  EVT_UPDATE_UI( idViewToolMain, MainFrame::OnViewMenuUpdateUI )
  EVT_UPDATE_UI( idViewLogManager, MainFrame::OnViewMenuUpdateUI )
  EVT_UPDATE_UI( idViewManager, MainFrame::OnViewMenuUpdateUI )
  EVT_UPDATE_UI( idViewStatusbar, MainFrame::OnViewMenuUpdateUI )
  EVT_UPDATE_UI( idViewScriptConsole, MainFrame::OnViewMenuUpdateUI )
  EVT_UPDATE_UI( idViewHideEditorTabs, MainFrame::OnViewMenuUpdateUI )
  EVT_UPDATE_UI( idViewFocusEditor, MainFrame::OnViewMenuUpdateUI )
  EVT_UPDATE_UI( idViewFocusManagement, MainFrame::OnViewMenuUpdateUI )
  EVT_UPDATE_UI( idViewFocusLogsAndOthers, MainFrame::OnViewMenuUpdateUI )
  EVT_UPDATE_UI( idViewFullScreen, MainFrame::OnViewMenuUpdateUI )
  EVT_MENU( idFileNewEmpty, MainFrame::OnFileNewWhat )
  EVT_MENU( idFileNewProject, MainFrame::OnFileNewWhat )
  EVT_MENU( idFileNewTarget, MainFrame::OnFileNewWhat )
  EVT_MENU( idFileNewFile, MainFrame::OnFileNewWhat )
  EVT_MENU( idFileNewCustom, MainFrame::OnFileNewWhat )
  EVT_MENU( idFileNewUser, MainFrame::OnFileNewWhat )
  EVT_MENU( idToolNew, MainFrame::OnFileNew )
  EVT_MENU( idFileOpen, MainFrame::OnFileOpen )
  EVT_MENU( idFileOpenRecentProjectClearHistory, MainFrame::OnFileOpenRecentProjectClearHistory )
  EVT_MENU( idFileOpenRecentFileClearHistory, MainFrame::OnFileOpenRecentClearHistory )
  EVT_MENU_RANGE( wxID_CBFILE01, wxID_CBFILE16, MainFrame::OnFileReopen )
  EVT_MENU_RANGE( wxID_CBFILE17, wxID_CBFILE32, MainFrame::OnFileReopenProject )
  EVT_MENU( idFileSave, MainFrame::OnFileSave )
  EVT_MENU( idFileSaveAs, MainFrame::OnFileSaveAs )
  EVT_MENU( idFileSaveProject, MainFrame::OnFileSaveProject )
  EVT_MENU( idFileSaveProjectAs, MainFrame::OnFileSaveProjectAs )
  EVT_MENU( idFileSaveProjectTemplate, MainFrame::OnFileSaveProjectTemplate )
  EVT_MENU( idFileOpenDefWorkspace, MainFrame::OnFileOpenDefWorkspace )
  EVT_MENU( idFileSaveWorkspace, MainFrame::OnFileSaveWorkspace )
  EVT_MENU( idFileSaveWorkspaceAs, MainFrame::OnFileSaveWorkspaceAs )
  EVT_MENU( idFileSaveAll, MainFrame::OnFileSaveAll )
  EVT_MENU( idFileCloseWorkspace, MainFrame::OnFileCloseWorkspace )
  EVT_MENU( idFileClose, MainFrame::OnFileClose )
  EVT_MENU( idFileCloseAll, MainFrame::OnFileCloseAll )
  EVT_MENU( idFileCloseProject, MainFrame::OnFileCloseProject )
  EVT_MENU( idFileExit, MainFrame::OnFileQuit )
  EVT_MENU( idFileNext, MainFrame::OnFileNext )
  EVT_MENU( idFilePrev, MainFrame::OnFilePrev )
  EVT_MENU( idEditUndo, MainFrame::OnEditUndo )
  EVT_MENU( idEditRedo, MainFrame::OnEditRedo )
  EVT_MENU( idEditClearHistory, MainFrame::OnEditClearHistory )
  EVT_MENU( idEditSwapHeaderSource, MainFrame::OnEditSwapHeaderSource )
  EVT_MENU( idEditGotoMatchingBrace, MainFrame::OnEditGotoMatchingBrace )
  EVT_MENU( idEditHighlightModeText, MainFrame::OnEditHighlightMode )
  EVT_MENU( idEditFoldAll, MainFrame::OnEditFoldAll )
  EVT_MENU( idEditUnfoldAll, MainFrame::OnEditUnfoldAll )
  EVT_MENU( idEditToggleAllFolds, MainFrame::OnEditToggleAllFolds )
  EVT_MENU( idEditFoldBlock, MainFrame::OnEditFoldBlock )
  EVT_MENU( idEditUnfoldBlock, MainFrame::OnEditUnfoldBlock )
  EVT_MENU( idEditToggleFoldBlock, MainFrame::OnEditToggleFoldBlock )
  EVT_MENU( idEditEncodingDefault, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUseBom, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingAscii, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUtf7, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUtf8, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUnicode, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUtf16, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUtf32, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUnicode16BE, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUnicode16LE, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUnicode32BE, MainFrame::OnEditEncoding )
  EVT_MENU( idEditEncodingUnicode32LE, MainFrame::OnEditEncoding )
  EVT_MENU( idEditParaUp, MainFrame::OnEditParaUp )
  EVT_MENU( idEditParaUpExtend, MainFrame::OnEditParaUpExtend )
  EVT_MENU( idEditParaDown, MainFrame::OnEditParaDown )
  EVT_MENU( idEditParaDownExtend, MainFrame::OnEditParaDownExtend )
  EVT_MENU( idEditWordPartLeft, MainFrame::OnEditWordPartLeft )
  EVT_MENU( idEditWordPartLeftExtend, MainFrame::OnEditWordPartLeftExtend )
  EVT_MENU( idEditWordPartRight, MainFrame::OnEditWordPartRight )
  EVT_MENU( idEditWordPartRightExtend, MainFrame::OnEditWordPartRightExtend )
  EVT_MENU( idEditZoomIn, MainFrame::OnEditZoomIn )
  EVT_MENU( idEditZoomOut, MainFrame::OnEditZoomOut )
  EVT_MENU( idEditZoomReset, MainFrame::OnEditZoomReset )
  EVT_MENU( idEditLineCut, MainFrame::OnEditLineCut )
  EVT_MENU( idEditLineDelete, MainFrame::OnEditLineDelete )
  EVT_MENU( idEditLineDuplicate, MainFrame::OnEditLineDuplicate )
  EVT_MENU( idEditLineTranspose, MainFrame::OnEditLineTranspose )
  EVT_MENU( idEditLineCopy, MainFrame::OnEditLineCopy )
  EVT_MENU( idEditLinePaste, MainFrame::OnEditLinePaste )
  EVT_MENU( idEditLineUp, MainFrame::OnEditLineMove )
  EVT_MENU( idEditLineDown, MainFrame::OnEditLineMove )
  EVT_MENU( idEditUpperCase, MainFrame::OnEditUpperCase )
  EVT_MENU( idEditLowerCase, MainFrame::OnEditLowerCase )
  EVT_MENU( idEditInsertNewLine, MainFrame::OnEditInsertNewLine )
  EVT_MENU( idEditGotoLineEnd, MainFrame::OnEditGotoLineEnd )
  EVT_MENU( idEditInsertNewLineBelow, MainFrame::OnEditInsertNewLineBelow )
  EVT_MENU( idEditInsertNewLineAbove, MainFrame::OnEditInsertNewLineAbove )
  EVT_MENU( idEditSelectAll, MainFrame::OnEditSelectAll )
  EVT_MENU( idEditSelectNext, MainFrame::OnEditSelectNext )
  EVT_MENU( idEditSelectNextSkip, MainFrame::OnEditSelectNextSkip )
  EVT_MENU( idEditBookmarksToggle, MainFrame::OnEditBookmarksToggle )
  EVT_MENU( idEditBookmarksNext, MainFrame::OnEditBookmarksNext )
  EVT_MENU( idEditBookmarksPrevious, MainFrame::OnEditBookmarksPrevious )
  EVT_MENU( idEditBookmarksClearAll, MainFrame::OnEditBookmarksClearAll )
  EVT_MENU( idEditCommentSelected, MainFrame::OnEditCommentSelected )
  EVT_MENU( idEditUncommentSelected, MainFrame::OnEditUncommentSelected )
  EVT_MENU( idEditToggleCommentSelected, MainFrame::OnEditToggleCommentSelected )
  EVT_MENU( idEditStreamCommentSelected, MainFrame::OnEditStreamCommentSelected )
  EVT_MENU( idEditBoxCommentSelected, MainFrame::OnEditBoxCommentSelected )
  EVT_MENU( idEditShowCallTip, MainFrame::OnEditShowCallTip )
  EVT_MENU( idEditCompleteCode, MainFrame::OnEditCompleteCode )
  EVT_MENU( idSearchFind, MainFrame::OnSearchFind )
  EVT_MENU( idSearchFindInFiles, MainFrame::OnSearchFind )
  EVT_MENU( idSearchFindNext, MainFrame::OnSearchFindNext )
  EVT_MENU( idSearchFindPrevious, MainFrame::OnSearchFindNext )
  EVT_MENU( idSearchFindSelectedNext, MainFrame::OnSearchFindNextSelected )
  EVT_MENU( idSearchFindSelectedPrevious, MainFrame::OnSearchFindNextSelected )
  EVT_MENU( idSearchReplace, MainFrame::OnSearchReplace )
  EVT_MENU( idSearchReplaceInFiles, MainFrame::OnSearchReplace )
  EVT_MENU( idSearchGotoLine, MainFrame::OnSearchGotoLine )
  EVT_MENU( idSearchGotoNextChanged, MainFrame::OnSearchGotoNextChanged )
  EVT_MENU( idSearchGotoPreviousChanged, MainFrame::OnSearchGotoPrevChanged )
  EVT_MENU( idViewLayoutSave, MainFrame::OnViewLayoutSave )
  EVT_MENU( idViewLayoutDelete, MainFrame::OnViewLayoutDelete )
  EVT_MENU( idViewToolFit, MainFrame::OnViewToolbarsFit )
  EVT_MENU( idViewToolOptimize, MainFrame::OnViewToolbarsOptimize )
  EVT_MENU( idViewToolMain, MainFrame::OnToggleBar )
  EVT_MENU( idViewToolDebugger, MainFrame::OnToggleBar )
  EVT_MENU( idViewLogManager, MainFrame::OnToggleBar )
  EVT_MENU( idViewManager, MainFrame::OnToggleBar )
  EVT_MENU( idViewStatusbar, MainFrame::OnToggleStatusBar )
  EVT_MENU( idViewScriptConsole, MainFrame::OnViewScriptConsole )
  EVT_MENU( idViewHideEditorTabs, MainFrame::OnViewHideEditorTabs )
  EVT_MENU( idViewFocusEditor, MainFrame::OnFocusEditor )
  EVT_MENU( idViewFocusManagement, MainFrame::OnFocusManagement )
  EVT_MENU( idViewFocusLogsAndOthers, MainFrame::OnFocusLogsAndOthers )
  EVT_MENU( idViewSwitchTabs, MainFrame::OnSwitchTabs )
  EVT_MENU( idViewFullScreen, MainFrame::OnToggleFullScreen )
  EVT_MENU( idSettingsEnvironment, MainFrame::OnSettingsEnvironment )
  EVT_MENU( idSettingsGlobalUserVars, MainFrame::OnGlobalUserVars )
  EVT_MENU( idSettingsEditor, MainFrame::OnSettingsEditor )
  EVT_MENU( idSettingsCompiler, MainFrame::OnSettingsCompiler )
  EVT_MENU( idSettingsDebugger, MainFrame::OnSettingsDebugger )
  EVT_MENU( idPluginsManagePlugins, MainFrame::OnSettingsPlugins )
  EVT_MENU( idSettingsScripting, MainFrame::OnSettingsScripting )
  EVT_MENU( idHelpTips, MainFrame::OnHelpTips )
  EVT_MENU( idStartHerePageLink, MainFrame::OnStartHereLink )
  EVT_CBAUIBOOK_LEFT_DCLICK( ID_NBEditorManager, MainFrame::OnNotebookDoubleClick )
  EVT_NOTEBOOK_PAGE_CHANGED( ID_NBEditorManager, MainFrame::OnPageChanged )
  EVT_BUTTON( idHighlightButton, MainFrame::OnHighlightMenu )
  EVT_BUTTON( idCloseFullScreen, MainFrame::OnToggleFullScreen )
  EVT_MENU( idShiftTab, MainFrame::OnShiftTab )
  EVT_MENU( idCtrlAltTab, MainFrame::OnCtrlAltTab )
  EVT_RIGHT_UP( MainFrame::OnMouseRightUp )
END_EVENT_TABLE()

MainFrame::MainFrame( wxWindow* parent )
  : wxFrame( parent, -1, _T( "MainWin" ), wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE ),
    m_LayoutManager( this ),
    m_pAccel( nullptr ),
    m_filesHistory( _T( "文件(&F)" ), _T( "/RECENT_FILES" ), idFileOpenRecentFileClearHistory, wxID_CBFILE01 ),
    m_projectsHistory( _T( "文件(&F)" ), _T( "/RECENT_PROJECTS" ), idFileOpenRecentProjectClearHistory, wxID_CBFILE17 ),
    m_pCloseFullScreenBtn( nullptr ),
    m_pEdMan( nullptr ),
    m_pPrjMan( nullptr ),
    m_pPrjManUI( nullptr ),
    m_pLogMan( nullptr ),
    m_pInfoPane( nullptr ),
    m_pToolbar( nullptr ),
    m_ToolsMenu( nullptr ),
    m_HelpPluginsMenu( nullptr ),
    m_ScanningForPlugins( false ),
    m_StartupDone( false ),
    m_InitiatedShutdown( false ),
    m_AutoHideLockCounter( 0 ),
    m_LastCtrlAltTabWindow( 0 ),
    m_LastLayoutIsTemp( false ),
    m_pScriptConsole( nullptr ),
    m_pBatchBuildDialog( nullptr ),
    m_pHighlightButton( nullptr ) {
  Manager::Get( this );
  RegisterEvents();
  SetDropTarget( new cbFileDropTarget( this ) );  // 允许文件拖拽
  wxAcceleratorEntry entries[8];
  entries[0].Set( wxACCEL_CTRL | wxACCEL_SHIFT, ( int ) 'W', idFileCloseAll );
  entries[1].Set( wxACCEL_CTRL | wxACCEL_SHIFT, WXK_F4, idFileCloseAll );
  entries[2].Set( wxACCEL_CTRL, ( int ) 'W', idFileClose );
  entries[3].Set( wxACCEL_CTRL, WXK_F4, idFileClose );
  entries[4].Set( wxACCEL_CTRL, WXK_F6, idFileNext );
  entries[5].Set( wxACCEL_CTRL | wxACCEL_SHIFT, WXK_F6, idFilePrev );
  entries[6].Set( wxACCEL_SHIFT, WXK_TAB, idShiftTab );
  entries[7].Set( wxACCEL_CTRL | wxACCEL_ALT, WXK_TAB, idCtrlAltTab );
  m_pAccel = new wxAcceleratorTable( 8, entries );
  SetAcceleratorTable( *m_pAccel );
  FileFilters::AddDefaultFileFilters();
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "app" ) );
  m_SmallToolBar = true;
  CreateIDE();
  DoCreateStatusBar();
  SetStatusText( _T( "欢迎来到墨世界" ) );
  wxStatusBar *sb = GetStatusBar();  //主状态栏
  if( sb ) {
    sb->Show( cfg->ReadBool( _T( "/main_frame/statusbar" ), true ) );
  }
  SetTitle( _T( "m" ) );
  LoadWindowSize();
  ScanForPlugins();
  CreateToolbars();
  Manager::Get()->GetCCManager();
  wxString deflayout = cfg->Read( _T( "/main_frame/layout/default" ) );
  if( deflayout.IsEmpty() ) {
    cfg->Write( _T( "/main_frame/layout/default" ), gDefaultLayout );
  }
  DoFixToolbarsLayout();
  gDefaultLayoutData = m_LayoutManager.SavePerspective();
  gDefaultMessagePaneLayoutData = m_pInfoPane->SaveTabOrder();
  SaveViewLayout( gDefaultLayout, gDefaultLayoutData, gDefaultMessagePaneLayoutData );
  wxAuiPaneInfoArray& panes = m_LayoutManager.GetAllPanes();
  for( size_t i = 0; i < panes.GetCount(); ++i ) {
    wxAuiPaneInfo& info = panes[i];
    if( !( info.name == _T( "MainPane" ) ) ) {
      info.Hide();
    }
  }
  gMinimalLayoutData = m_LayoutManager.SavePerspective();
  gMinimalMessagePaneLayoutData = m_pInfoPane->SaveTabOrder();
  SaveViewLayout( gMinimalLayout, gMinimalLayoutData, gMinimalMessagePaneLayoutData );
  LoadWindowState();
  RegisterScriptFunctions();
  RunStartupScripts();
}

MainFrame::~MainFrame() {
  SetAcceleratorTable( wxNullAcceleratorTable );
  delete m_pAccel;
  delete m_debuggerMenuHandler;
  delete m_debuggerToolbarHandler;
}

void MainFrame::RegisterEvents() {
  Manager* m = Manager::Get();
  m->RegisterEventSink( cbEVT_EDITOR_UPDATE_UI, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnEditorUpdateUI ) );
  m->RegisterEventSink( cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnProjectActivated ) );
  m->RegisterEventSink( cbEVT_PROJECT_OPEN, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnProjectOpened ) );
  m->RegisterEventSink( cbEVT_PROJECT_CLOSE, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnProjectClosed ) );
  m->RegisterEventSink( cbEVT_EDITOR_CLOSE, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnEditorClosed ) );
  m->RegisterEventSink( cbEVT_EDITOR_OPEN, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnEditorOpened ) );
  m->RegisterEventSink( cbEVT_EDITOR_ACTIVATED, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnEditorActivated ) );
  m->RegisterEventSink( cbEVT_EDITOR_SAVE, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnEditorSaved ) );
  m->RegisterEventSink( cbEVT_EDITOR_MODIFIED, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnEditorModified ) );
  m->RegisterEventSink( cbEVT_ADD_DOCK_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>( this, &MainFrame::OnRequestDockWindow ) );
  m->RegisterEventSink( cbEVT_REMOVE_DOCK_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>( this, &MainFrame::OnRequestUndockWindow ) );
  m->RegisterEventSink( cbEVT_SHOW_DOCK_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>( this, &MainFrame::OnRequestShowDockWindow ) );
  m->RegisterEventSink( cbEVT_HIDE_DOCK_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>( this, &MainFrame::OnRequestHideDockWindow ) );
  m->RegisterEventSink( cbEVT_DOCK_WINDOW_VISIBILITY, new cbEventFunctor<MainFrame, CodeBlocksDockEvent>( this, &MainFrame::OnDockWindowVisibility ) );
  m->RegisterEventSink( cbEVT_APP_CMDLINE, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnPluginLoaded ) );
  m->RegisterEventSink( cbEVT_PLUGIN_RELEASED, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnPluginUnloaded ) );
  m->RegisterEventSink( cbEVT_PLUGIN_INSTALLED, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnPluginInstalled ) );
  m->RegisterEventSink( cbEVT_PLUGIN_UNINSTALLED, new cbEventFunctor<MainFrame, CodeBlocksEvent>( this, &MainFrame::OnPluginUninstalled ) );
  m->RegisterEventSink( cbEVT_UPDATE_VIEW_LAYOUT, new cbEventFunctor<MainFrame, CodeBlocksLayoutEvent>( this, &MainFrame::OnLayoutUpdate ) );
  m->RegisterEventSink( cbEVT_QUERY_VIEW_LAYOUT, new cbEventFunctor<MainFrame, CodeBlocksLayoutEvent>( this, &MainFrame::OnLayoutQuery ) );
  m->RegisterEventSink( cbEVT_SWITCH_VIEW_LAYOUT, new cbEventFunctor<MainFrame, CodeBlocksLayoutEvent>( this, &MainFrame::OnLayoutSwitch ) );
  m->RegisterEventSink( cbEVT_ADD_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>( this, &MainFrame::OnAddLogWindow ) );
  m->RegisterEventSink( cbEVT_REMOVE_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>( this, &MainFrame::OnRemoveLogWindow ) );
  m->RegisterEventSink( cbEVT_HIDE_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>( this, &MainFrame::OnHideLogWindow ) );
  m->RegisterEventSink( cbEVT_SWITCH_TO_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>( this, &MainFrame::OnSwitchToLogWindow ) );
  m->RegisterEventSink( cbEVT_GET_ACTIVE_LOG_WINDOW, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>( this, &MainFrame::OnGetActiveLogWindow ) );
  m->RegisterEventSink( cbEVT_SHOW_LOG_MANAGER, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>( this, &MainFrame::OnShowLogManager ) );
  m->RegisterEventSink( cbEVT_HIDE_LOG_MANAGER, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>( this, &MainFrame::OnHideLogManager ) );
  m->RegisterEventSink( cbEVT_LOCK_LOG_MANAGER, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>( this, &MainFrame::OnLockLogManager ) );
  m->RegisterEventSink( cbEVT_UNLOCK_LOG_MANAGER, new cbEventFunctor<MainFrame, CodeBlocksLogEvent>( this, &MainFrame::OnUnlockLogManager ) );
}

void MainFrame::ShowTips( bool forceShow ) {
  bool showAtStartup = Manager::Get()->GetConfigManager( _T( "app" ) )->ReadBool( _T( "/show_tips" ), false );
  if( forceShow || showAtStartup ) {
    wxString tipsFile = ConfigManager::GetDataFolder() + _T( "/tips.txt" );
    long tipsIndex = Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/next_tip" ), 0 );
    wxTipProvider* tipProvider = wxCreateFileTipProvider( tipsFile, tipsIndex );
    showAtStartup = wxShowTip( this, tipProvider, showAtStartup );
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/show_tips" ), showAtStartup );
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/next_tip" ), ( int )tipProvider->GetCurrentTip() );
    delete tipProvider;
  }
}

void MainFrame::CreateIDE() {
  int leftW = Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/main_frame/layout/left_block_width" ), 200 );
  wxSize clientsize = GetClientSize();
  m_pCloseFullScreenBtn = new wxButton( this, idCloseFullScreen, _T( "关闭全屏" ), wxDefaultPosition );
  m_pCloseFullScreenBtn->Show( false );
  m_pPrjMan = Manager::Get()->GetProjectManager();
  if( !Manager::IsBatchBuild() ) {
    m_pPrjManUI = new ProjectManagerUI;
    m_LayoutManager.AddPane( m_pPrjManUI->GetNotebook(), wxAuiPaneInfo().Name( _T( "ManagementPane" ) ).Caption( _T( "管理面板" ) ).
                             BestSize( wxSize( leftW, clientsize.GetHeight() ) ).MinSize( wxSize( 260, 100 ) ).Left().Layer( 1 ) );
  } else {
    m_pPrjManUI = new BatchProjectManagerUI;
  }
  m_pPrjMan->SetUI( m_pPrjManUI );
  SetupGUILogging();
  SetupDebuggerUI();
  CreateMenubar();
  m_pEdMan = Manager::Get()->GetEditorManager();
  m_pLogMan = Manager::Get()->GetLogManager();
  m_LayoutManager.AddPane( m_pEdMan->GetNotebook(), wxAuiPaneInfo().Name( _T( "MainPane" ) ).CentrePane() );
  m_pScriptConsole = new ScriptConsole( this, -1 );
  m_LayoutManager.AddPane( m_pScriptConsole, wxAuiPaneInfo().Name( _T( "ScriptConsole" ) ).Caption( _T( "脚本控制台" ) ).Float().MinSize( 100, 100 ).FloatingPosition( 300, 200 ).Hide() );
  DoUpdateLayout();
  DoUpdateLayoutColours();
  DoUpdateEditorStyle();
  m_pEdMan->GetNotebook()->SetDropTarget( new cbFileDropTarget( this ) );
  if( m_pPrjManUI->GetNotebook() ) {
    m_pPrjManUI->GetNotebook()->SetDropTarget( new cbFileDropTarget( this ) );
  }
  Manager::Get()->GetColourManager()->Load();
}

void MainFrame::SetupGUILogging() {
  m_LayoutManager.SetDockSizeConstraint( 0.75, 0.75 );
  int bottomH = Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/main_frame/layout/bottom_block_height" ), 260 );
  wxSize clientsize = GetClientSize();
  LogManager* mgr = Manager::Get()->GetLogManager();
  if( !Manager::IsBatchBuild() ) {
    m_pInfoPane = new InfoPane( this );
    m_LayoutManager.AddPane( m_pInfoPane, wxAuiPaneInfo(). Name( _T( "MessagesPane" ) ).Caption( _T( "信息栏" ) ). BestSize( wxSize( clientsize.GetWidth(), bottomH ) ).Bottom() );
    wxWindow* log;
    for( size_t i = LogManager::app_log; i < LogManager::max_logs; ++i ) {
      if( ( log = mgr->Slot( i ).GetLogger()->CreateControl( m_pInfoPane ) ) ) {
        m_pInfoPane->AddLogger( mgr->Slot( i ).GetLogger(), log, mgr->Slot( i ).title, mgr->Slot( i ).icon );
      }
    }
  } else {
    m_pBatchBuildDialog = new BatchLogWindow( this, _T( "日志" ) );
    wxSizer* s = new wxBoxSizer( wxVERTICAL );
    m_pInfoPane = new InfoPane( m_pBatchBuildDialog );
    s->Add( m_pInfoPane, 1, wxEXPAND );
    m_pBatchBuildDialog->SetSizer( s );
    mgr->SetLog( new NullLogger, LogManager::app_log );
    mgr->SetLog( new NullLogger, LogManager::debug_log );
  }
  mgr->NotifyUpdate();
  m_pInfoPane->SetDropTarget( new cbFileDropTarget( this ) );
}

void MainFrame::SetupDebuggerUI() {
  m_debuggerMenuHandler = new DebuggerMenuHandler;
  m_debuggerToolbarHandler = new DebuggerToolbarHandler( m_debuggerMenuHandler );
  m_debuggerMenuHandler->SetEvtHandlerEnabled( false );
  m_debuggerToolbarHandler->SetEvtHandlerEnabled( false );
  wxWindow* window = Manager::Get()->GetAppWindow();
  if( window ) {
    window->PushEventHandler( m_debuggerMenuHandler );
    window->PushEventHandler( m_debuggerToolbarHandler );
  }
  m_debuggerMenuHandler->SetEvtHandlerEnabled( true );
  m_debuggerToolbarHandler->SetEvtHandlerEnabled( true );
  if( !Manager::IsBatchBuild() ) {
    Manager::Get()->GetDebuggerManager()->SetInterfaceFactory( new DebugInterfaceFactory );
    m_debuggerMenuHandler->RegisterDefaultWindowItems();
  }
}

DECLARE_INSTANCE_TYPE( MainFrame );

void MainFrame::RegisterScriptFunctions() {
  SqPlus::SQClassDef<MainFrame>( "MainFrame" ).
  func( &MainFrame::Open, "Open" );
  SqPlus::BindVariable( this, "App", SqPlus::VAR_ACCESS_READ_ONLY );
}

void MainFrame::RunStartupScripts() {
  ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "scripting" ) );
  wxArrayString keys = mgr->EnumerateKeys( _T( "/startup_scripts" ) );
  for( size_t i = 0; i < keys.GetCount(); ++i ) {
    ScriptEntry se;
    wxString ser;
    if( mgr->Read( _T( "/startup_scripts/" ) + keys[i], &ser ) ) {
      se.SerializeIn( ser );
      if( !se.enabled ) {
        continue;
      }
      try {
        wxString startup = se.script;
        if( wxFileName( se.script ).IsRelative() ) {
          startup = ConfigManager::LocateDataFile( se.script, sdScriptsUser | sdScriptsGlobal );
        }
        if( !startup.IsEmpty() ) {
          if( !se.registered ) {
            Manager::Get()->GetScriptingManager()->LoadScript( startup );
          } else if( !se.menu.IsEmpty() ) {
            Manager::Get()->GetScriptingManager()->RegisterScriptMenu( se.menu, startup, false );
          } else {
            Manager::Get()->GetLogManager()->LogWarning( F( _T( "启动 脚本/函数 '%s' 未加载: 无效配置" ), se.script.wx_str() ) );
          }
        } else {
          Manager::Get()->GetLogManager()->LogWarning( F( _T( "启动脚本 '%s' 找不到" ), se.script.wx_str() ) );
        }
      } catch( SquirrelError& exception ) {
        Manager::Get()->GetScriptingManager()->DisplayErrors( &exception );
      }
    }
  }
}

void MainFrame::PluginsUpdated( M_Code_unused M_Code_Plugin* plugin, M_Code_unused int status ) {
  Freeze();
  RecreateMenuBar();
  PluginElementsArray plugins = Manager::Get()->GetPluginManager()->GetPlugins();
  for( unsigned int i = 0; i < plugins.GetCount(); ++i ) {
    M_Code_Plugin* plug = plugins[i]->plugin;
    const PluginInfo* info = Manager::Get()->GetPluginManager()->GetPluginInfo( plug );
    if( !info ) {
      continue;
    }
    if( m_PluginsTools[plug] ) {
      wxMenu* viewToolbars = nullptr;
      GetMenuBar()->FindItem( idViewToolMain, &viewToolbars );
      if( viewToolbars ) {
        if( viewToolbars->FindItem( info->title ) != wxNOT_FOUND ) {
          continue;
        }
        wxMenuItem* item = AddPluginInMenus( viewToolbars, plug,
                                             ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )&MainFrame::OnToggleBar, -1, true );
        if( item ) {
          item->Check( IsWindowReallyShown( m_PluginsTools[plug] ) );
        }
      }
    }
  }
  Thaw();
}

void MainFrame::RecreateMenuBar() {
  Freeze();
  wxMenuBar* m = GetMenuBar();
  SetMenuBar( nullptr );
  CreateMenubar();
  delete m;
  for( LayoutViewsMap::iterator it = m_LayoutViews.begin(); it != m_LayoutViews.end(); ++it ) {
    if( it->first.IsEmpty() ) {
      continue;
    }
    SaveViewLayout( it->first, it->second, m_LayoutMessagePane[it->first], it->first == m_LastLayoutName );
  }
  Thaw();
}

void MainFrame::CreateMenubar() {
  CodeBlocksEvent event( cbEVT_MENUBAR_CREATE_BEGIN );
  Manager::Get()->ProcessEvent( event );
  int tmpidx;
  wxMenuBar* mbar = nullptr;
  wxMenu *hl = nullptr, *tools = nullptr, *plugs = nullptr, *pluginsM = nullptr;
  wxMenuItem *tmpitem = nullptr;
  wxXmlResource* xml_res = wxXmlResource::Get();
  wxString resPath = ConfigManager::GetDataFolder() + _T( "/主菜单.xrc" );
  if( !xml_res->Load( resPath ) )  {
    Manager::Get()->GetLogManager()->LogInf( F( _T( "文件丢失: %s " ), resPath.wx_str() ) );
    return;
  }
  mbar = xml_res->LoadMenuBar( _T( "main_menu_bar" ) );
  if( mbar ) {
    tmpidx = mbar->FindMenu( _T( "编辑(&E)" ) );
    if( tmpidx != wxNOT_FOUND ) {
      mbar->FindItem( idEditHighlightModeText, &hl );
      if( hl ) {
        EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
        if( colour_set ) {
          wxArrayString langs = colour_set->GetAllHighlightLanguages();
          for( size_t i = 0; i < langs.GetCount(); ++i ) {
            if( i > 0 && !( i % 20 ) ) {
              hl->Break();
            }
            int id = wxNewId();
            hl->AppendRadioItem( id, langs[i], F( _T( "高亮显示模式切换为 \"%s\"" ), langs[i].wx_str() ) );
            Connect( id, -1, wxEVT_COMMAND_MENU_SELECTED, ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction ) &MainFrame::OnEditHighlightMode );
          }
        }
      }
      const wxLanguageInfo* info = wxLocale::GetLanguageInfo( wxLANGUAGE_DEFAULT );
      wxMenu* editMenu = mbar->GetMenu( tmpidx );
      if( info && ( ( info->Language >= wxLANGUAGE_CHINESE && info->Language <= wxLANGUAGE_CHINESE_TAIWAN )
                    || info->Language == wxLANGUAGE_JAPANESE
                    || info->Language == wxLANGUAGE_KOREAN ) ) {
        editMenu->Append( idEditCompleteCode, _T( "完整代码\tShift-Space" ) );
      } else {
        editMenu->Append( idEditCompleteCode, _T( "完整代码\tCtrl-Space" ) );
      }
    }
    tmpidx = mbar->FindMenu( _T( "工具(&T)" ) );
    if( tmpidx != wxNOT_FOUND ) {
      tools = mbar->GetMenu( tmpidx );
    }
    tmpidx = mbar->FindMenu( _T( "插件(&l)" ) );
    if( tmpidx != wxNOT_FOUND ) {
      plugs = mbar->GetMenu( tmpidx );
    }
    if( ( tmpitem = mbar->FindItem( idHelpPlugins, nullptr ) ) ) {
      pluginsM = tmpitem->GetSubMenu();
    }
    m_ToolsMenu = tools ? tools : new wxMenu();
    m_PluginsMenu = plugs ? plugs : new wxMenu();
    m_HelpPluginsMenu = pluginsM ? pluginsM : new wxMenu();
    Manager::Get()->GetDebuggerManager()->SetMenuHandler( m_debuggerMenuHandler );
    PluginElementsArray plugins = Manager::Get()->GetPluginManager()->GetPlugins();
    for( unsigned int i = 0; i < plugins.GetCount(); ++i ) {
      M_Code_Plugin* plug = plugins[i]->plugin;
      if( plug && plug->IsAttached() ) {
        if( plug->GetType() == ptTool ) {
          DoAddPlugin( plug );
        } else {
          try {
            plug->BuildMenu( mbar );
          } catch( M_Code_Exception& e ) {
            e.ShowErrorMessage();
          }
        }
      }
    }
    Manager::Get()->GetToolsManager()->BuildToolsMenu( m_ToolsMenu );
    SetMenuBar( mbar );
    InitializeRecentFilesHistory();
    CodeBlocksEvent event2( cbEVT_MENUBAR_CREATE_END );
    Manager::Get()->ProcessEvent( event2 );
    SetMenuBar( mbar );
  } else {
    Manager::Get()->GetLogManager()->LogInf( F( _T( "XRC错误: %s " ), resPath.wx_str() ) );
    Close();
  }
}

void MainFrame::CreateToolbars() {
  if( m_pToolbar ) {
    SetToolBar( nullptr );
    m_pToolbar = nullptr;
  }
  wxXmlResource* xml_res = wxXmlResource::Get();
  wxString resPath = ConfigManager::GetDataFolder() + _T( "/主工具栏.xrc" );
  if( !xml_res->Load( resPath ) )  {
    Manager::Get()->GetLogManager()->LogInf( F( _T( "文件丢失: %s " ), resPath.wx_str() ) );
    Close( );
  }
  m_pToolbar = Manager::Get()->CreateEmptyToolbar();
  Manager::Get()->AddonToolBar( m_pToolbar, _T( "main_toolbar" ) );
  m_pToolbar->Realize();
  m_pToolbar->Connect( wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEventHandler( MainFrame::OnToolBarRightClick ), NULL, this );
  m_pToolbar->SetInitialSize();
  m_debuggerToolbarHandler->GetToolbar()->Connect( wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEventHandler( MainFrame::OnToolBarRightClick ), NULL, this );
  std::vector<ToolbarInfo> toolbars;
  toolbars.push_back( ToolbarInfo( m_pToolbar, wxAuiPaneInfo().Name( _T( "MainToolbar" ) ).Caption( _T( "Main Toolbar" ) ), 0 ) );
  toolbars.push_back( ToolbarInfo( m_debuggerToolbarHandler->GetToolbar(), wxAuiPaneInfo(). Name( _T( "DebuggerToolbar" ) ).Caption( _T( "Debugger Toolbar" ) ), 2 ) );
  PluginElementsArray plugins = Manager::Get()->GetPluginManager()->GetPlugins();
  for( unsigned int i = 0; i < plugins.GetCount(); ++i ) {
    M_Code_Plugin* plug = plugins[i]->plugin;
    if( plug && plug->IsAttached() ) {
      ToolbarInfo info = DoAddPluginToolbar( plug );
      if( info.toolbar ) {
        toolbars.push_back( info );
        info.toolbar->Connect( wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEventHandler( MainFrame::OnToolBarRightClick ), NULL, this );
      }
    }
  }
  std::sort( toolbars.begin(), toolbars.end() );
  int row = 0, position = 0, rowLength = 0;
  int maxLength = GetSize().x;
  for( std::vector<ToolbarInfo>::iterator it = toolbars.begin(); it != toolbars.end(); ++it ) {
    rowLength += it->toolbar->GetSize().x;
    if( rowLength >= maxLength ) {
      row++;
      position = 0;
      rowLength = it->toolbar->GetSize().x;
    }
    wxAuiPaneInfo paneInfo( it->paneInfo );
    m_LayoutManager.AddPane( it->toolbar, paneInfo.ToolbarPane().Top().Row( row ).Position( position ) );
    position += it->toolbar->GetSize().x;
  }
  DoUpdateLayout();
  Manager::ProcessPendingEvents();
  SetToolBar( nullptr );
}

void MainFrame::AddToolbarItem( int id, const wxString& title, const wxString& shortHelp, const wxString& longHelp, const wxString& image ) {
  m_pToolbar->AddTool( id, title, M_Code_LoadBitmap( image, wxBITMAP_TYPE_PNG ) );
  m_pToolbar->SetToolShortHelp( id, shortHelp );
  m_pToolbar->SetToolLongHelp( id, longHelp );
}

void MainFrame::ScanForPlugins() {
  m_ScanningForPlugins = true;
  m_PluginIDsMap.clear();
  PluginManager* m_PluginManager = Manager::Get()->GetPluginManager();
  wxString path = ConfigManager::GetPluginsFolder( true );
  Manager::Get()->GetLogManager()->Log( _T( "正在扫描插件 " ) + path );
  int count = m_PluginManager->ScanForPlugins( path );
  if( count > 0 ) {
    m_PluginManager->LoadAllPlugins();
  }
  CodeBlocksEvent event( cbEVT_PLUGIN_LOADING_COMPLETE );
  Manager::Get()->GetPluginManager()->NotifyPlugins( event );
  m_ScanningForPlugins = false;
}

wxMenuItem* MainFrame::AddPluginInMenus( wxMenu* menu, M_Code_Plugin* plugin, wxObjectEventFunction callback, int pos, bool checkable ) {
  wxMenuItem* item = nullptr;
  if( !plugin || !menu ) {
    return item;
  }
  const PluginInfo* info = Manager::Get()->GetPluginManager()->GetPluginInfo( plugin );
  if( !info ) {
    return nullptr;
  }
  PluginIDsMap::iterator it;
  for( it = m_PluginIDsMap.begin(); it != m_PluginIDsMap.end(); ++it ) {
    if( it->second == info->name ) {
      item = menu->FindItem( it->first );
      if( item ) {
        return item;
      }
    }
  }
  int id = wxNewId();
  wxString title = info->title + ( menu == m_HelpPluginsMenu ? _T( "..." ) : wxEmptyString );
  m_PluginIDsMap[id] = info->name;
  if( pos == -1 ) {
    pos = menu->GetMenuItemCount();
  }
  while( !item ) {
    if( !pos || title.CmpNoCase( menu->FindItemByPosition( pos - 1 )->GetLabel() ) > 0 ) {
      item = menu->Insert( pos, id, title, wxEmptyString, checkable ? wxITEM_CHECK : wxITEM_NORMAL );
    }
    --pos;
  }
  Connect( id, wxEVT_COMMAND_MENU_SELECTED, callback );
  return item;
}

void MainFrame::AddPluginInPluginsMenu( M_Code_Plugin* plugin ) {
  if( m_PluginsMenu->GetMenuItemCount() == 1 ) {
    m_PluginsMenu->Insert( 0, wxID_SEPARATOR, _T( "" ) );
  }
  AddPluginInMenus( m_PluginsMenu, plugin,
                    ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )&MainFrame::OnPluginsExecuteMenu,
                    m_PluginsMenu->GetMenuItemCount() - 2 );
}

void MainFrame::LoadWindowState() {
  wxArrayString subs = Manager::Get()->GetConfigManager( _T( "app" ) )->EnumerateSubPaths( _T( "/main_frame/layout" ) );
  for( size_t i = 0; i < subs.GetCount(); ++i ) {
    wxString name = Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "/main_frame/layout/" ) + subs[i] + _T( "/name" ) );
    wxString layout = Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "/main_frame/layout/" ) + subs[i] + _T( "/data" ) );
    wxString layoutMP = Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "/main_frame/layout/" ) + subs[i] + _T( "/dataMessagePane" ) );
    SaveViewLayout( name, layout, layoutMP );
  }
  wxString deflayout = Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "/main_frame/layout/default" ) );
  LoadViewLayout( deflayout );
  if( m_pPrjManUI->GetNotebook() ) {
    m_pPrjManUI->GetNotebook()->SetSelection( Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/main_frame/layout/left_block_selection" ), 0 ) );
  }
  m_pInfoPane->SetSelection( Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/main_frame/layout/bottom_block_selection" ), 0 ) );
  if( Manager::Get()->GetConfigManager( _T( "app" ) )->ReadBool( _T( "/environment/infopane_tabs_bottom" ), false ) ) {
    m_pInfoPane->SetWindowStyleFlag( m_pInfoPane->GetWindowStyleFlag() | wxAUI_NB_BOTTOM );
  }
}

void MainFrame::LoadWindowSize() {
  int x = 42;
  int y = 404;
  int w = 1860;
  int h = 1010;
  int last_display_index = Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/main_frame/layout/display" ), 0 );
  wxRect rect( Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/main_frame/layout/left" ), x ),
               Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/main_frame/layout/top" ), y ),
               Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/main_frame/layout/width" ), w ),
               Manager::Get()->GetConfigManager( _T( "app" ) )->ReadInt( _T( "/main_frame/layout/height" ), h ) );
  bool maximized = Manager::Get()->GetConfigManager( _T( "app" ) )->ReadBool( _T( "/main_frame/layout/maximized" ), false );
  Maximize( maximized );
  int display_index_window = wxDisplay::GetFromWindow( this );
  if( ( last_display_index >= 0 ) && ( last_display_index < static_cast<int>( wxDisplay::GetCount() ) ) ) {
    display_index_window = static_cast<int>( last_display_index );
  }
  int display_index = ( ( display_index_window >= 0 ) ? display_index_window : 0 );
  wxDisplay disp( display_index );
  if( maximized ) {
    rect = disp.GetClientArea();
    rect.width -= 100;
    rect.height -= 100;
  } else {
    wxRect displayRect = disp.GetClientArea();
    if( ( displayRect.GetLeft() + displayRect.GetWidth() ) < rect.GetLeft() ) {
      rect.SetLeft( displayRect.GetLeft() );
    }
    if( ( displayRect.GetLeft() + displayRect.GetWidth() ) < rect.GetRight() ) {
      rect.SetRight( displayRect.GetRight() );
    }
    if( ( displayRect.GetTop() + displayRect.GetHeight() ) < rect.GetTop() ) {
      rect.SetTop( displayRect.GetTop() );
    }
    if( ( displayRect.GetTop() + displayRect.GetHeight() ) < rect.GetBottom() ) {
      rect.SetBottom( displayRect.GetBottom() );
    }
  }
  SetSize( rect );
}

void MainFrame::SaveWindowState() {
  DoCheckCurrentLayoutForChanges( false );
  wxArrayString subs = Manager::Get()->GetConfigManager( _T( "app" ) )->EnumerateSubPaths( _T( "/main_frame/layout" ) );
  for( size_t i = 0; i < subs.GetCount(); ++i ) {
    Manager::Get()->GetConfigManager( _T( "app" ) )->DeleteSubPath( _T( "/main_frame/layout/" ) + subs[i] );
  }
  int count = 0;
  for( LayoutViewsMap::iterator it = m_LayoutViews.begin(); it != m_LayoutViews.end(); ++it ) {
    if( it->first.IsEmpty() ) {
      continue;
    }
    ++count;
    wxString key = F( _T( "/main_frame/layout/view%d/" ), count );
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( key + _T( "name" ), it->first );
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( key + _T( "data" ), it->second );
    if( !m_LayoutMessagePane[it->first].IsEmpty() ) {
      Manager::Get()->GetConfigManager( _T( "app" ) )->Write( key + _T( "dataMessagePane" ), m_LayoutMessagePane[it->first] );
    }
  }
  if( m_pPrjManUI->GetNotebook() ) {
    int selection = m_pPrjManUI->GetNotebook()->GetSelection();
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/main_frame/layout/left_block_selection" ), selection );
  }
  Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/main_frame/layout/bottom_block_selection" ), m_pInfoPane->GetSelection() );
  Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/main_frame/layout/display" ), wxDisplay::GetFromWindow( this ) );
  if( !IsMaximized() && !IsIconized() ) {
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/main_frame/layout/left" ), GetPosition().x );
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/main_frame/layout/top" ), GetPosition().y );
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/main_frame/layout/width" ), GetSize().x );
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/main_frame/layout/height" ), GetSize().y );
  }
  Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/main_frame/layout/maximized" ), IsMaximized() );
}

void MainFrame::LoadViewLayout( const wxString& name, bool isTemp ) {
  if( m_LastLayoutName != name && !DoCheckCurrentLayoutForChanges( true ) ) {
    return;
  }
  m_LastLayoutIsTemp = isTemp;
  wxString layout = m_LayoutViews[name];
  wxString layoutMP = m_LayoutMessagePane[name];
  if( layoutMP.IsEmpty() ) {
    layoutMP = m_LayoutMessagePane[gDefaultLayout];
  }
  if( layout.IsEmpty() ) {
    layout = m_LayoutViews[gDefaultLayout];
    SaveViewLayout( name, layout, layoutMP, false );
    DoSelectLayout( name );
  } else {
    DoSelectLayout( name );
  }
  m_pInfoPane->LoadTabOrder( layoutMP );
  m_LayoutManager.LoadPerspective( layout, false );
  DoUpdateLayout();
  m_PreviousLayoutName = m_LastLayoutName;
  m_LastLayoutName = name;
  m_LastLayoutData = layout;
  m_LastMessagePaneLayoutData = layoutMP;
  CodeBlocksLayoutEvent evt( cbEVT_SWITCHED_VIEW_LAYOUT );
  evt.layout = name;
  Manager::Get()->ProcessEvent( evt );
}

void MainFrame::SaveViewLayout( const wxString& name, const wxString& layout, const wxString& layoutMP, bool select ) {
  if( name.IsEmpty() ) {
    return;
  }
  m_LayoutViews[name] = layout;
  m_LayoutMessagePane[name] = layoutMP;
  wxMenu* viewLayouts = nullptr;
  GetMenuBar()->FindItem( idViewLayoutSave, &viewLayouts );
  if( viewLayouts && viewLayouts->FindItem( name ) == wxNOT_FOUND ) {
    int id = wxNewId();
    viewLayouts->InsertCheckItem( viewLayouts->GetMenuItemCount() - 3, id, name, F( _T( "切换到 %s 观点" ), name.wx_str() ) );
    Connect( id, wxEVT_COMMAND_MENU_SELECTED, ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )&MainFrame::OnViewLayout );
    m_PluginIDsMap[id] = name;
  }
  if( select ) {
    DoSelectLayout( name );
    m_LastLayoutName = name;
  }
}

bool MainFrame::LayoutDifferent( const wxString& layout1, const wxString& layout2, const wxString& delimiter ) {
  wxStringTokenizer strTok;
  unsigned long j;
  strTok.SetString( layout1, delimiter );
  wxArrayString arLayout1;
  while( strTok.HasMoreTokens() ) {
    wxStringTokenizer strTokColon( strTok.GetNextToken(), _T( ";" ) );
    while( strTokColon.HasMoreTokens() ) {
      wxString theToken = strTokColon.GetNextToken();
      if( theToken.StartsWith( _T( "state=" ) ) ) {
        theToken = theToken.Right( theToken.Len() - wxString( _T( "state=" ) ).Len() );
        theToken.ToULong( &j );
        theToken = F( _T( "state=%lu" ), j & wxAuiPaneInfo::optionHidden );
      }
      arLayout1.Add( theToken );
    }
  }
  strTok.SetString( layout2, delimiter );
  wxArrayString arLayout2;
  while( strTok.HasMoreTokens() ) {
    wxStringTokenizer strTokColon( strTok.GetNextToken(), _T( ";" ) );
    while( strTokColon.HasMoreTokens() ) {
      wxString theToken = strTokColon.GetNextToken();
      if( theToken.StartsWith( _T( "state=" ) ) ) {
        theToken = theToken.Right( theToken.Len() - wxString( _T( "state=" ) ).Len() );
        theToken.ToULong( &j );
        theToken = F( _T( "state=%lu" ), j & wxAuiPaneInfo::optionHidden );
      }
      arLayout2.Add( theToken );
    }
  }
  arLayout1.Sort();
  arLayout2.Sort();
  return arLayout1 != arLayout2;
}

bool MainFrame::LayoutMessagePaneDifferent( const wxString& layout1, const wxString& layout2, bool checkSelection ) {
  wxStringTokenizer strTok;
  wxArrayString arLayout1;
  wxArrayString arLayout2;
  strTok.SetString( layout1.BeforeLast( '|' ), _T( ";" ) );
  while( strTok.HasMoreTokens() ) {
    arLayout1.Add( strTok.GetNextToken() );
  }
  strTok.SetString( layout2.BeforeLast( '|' ), _T( ";" ) );
  while( strTok.HasMoreTokens() ) {
    arLayout2.Add( strTok.GetNextToken() );
  }
  if( checkSelection ) {
    arLayout1.Add( layout1.AfterLast( '|' ) );
    arLayout2.Add( layout2.AfterLast( '|' ) );
  }
  arLayout1.Sort();
  arLayout2.Sort();
  return arLayout1 != arLayout2;
}

bool MainFrame::DoCheckCurrentLayoutForChanges( bool canCancel ) {
  DoFixToolbarsLayout();
  wxString lastlayout = m_LayoutManager.SavePerspective();
  wxString lastmessagepanelayout = m_pInfoPane->SaveTabOrder();
  if( !m_LastLayoutName.IsEmpty() && ( LayoutDifferent( lastlayout, m_LastLayoutData ) ||
                                       LayoutMessagePaneDifferent( lastmessagepanelayout, m_LastMessagePaneLayoutData,
                                           Manager::Get()->GetConfigManager( _T( "message_manager" ) )->ReadBool( _T( "/save_selection_change_in_mp" ), true ) ) ) ) {
    Manager::Get()->GetLogManager()->Log( F( _T( "视图 '%s' 已更改?" ), m_LastLayoutName.wx_str() ) );
    SaveViewLayout( m_LastLayoutName, lastlayout, lastmessagepanelayout, false );
  }
  return true;
}

void MainFrame::DoFixToolbarsLayout() {
  wxAuiPaneInfoArray& panes = m_LayoutManager.GetAllPanes();
  for( size_t i = 0; i < panes.GetCount(); ++i ) {
    wxAuiPaneInfo& info = panes[i];
    if( info.state & wxAuiPaneInfo::optionToolbar ) {
      info.best_size = info.window->GetBestSize();
      info.floating_size = wxDefaultSize;
    }
  }
}

void MainFrame::DoSelectLayout( const wxString& name ) {
  wxMenu* viewLayouts = nullptr;
  GetMenuBar()->FindItem( idViewLayoutSave, &viewLayouts );
  if( viewLayouts ) {
    wxMenuItemList& items = viewLayouts->GetMenuItems();
    for( size_t i = 0; i < items.GetCount(); ++i ) {
      if( !items[i]->IsCheckable() ) {
        continue;
      }
      items[i]->Check( items[i]->GetText().IsSameAs( name ) );
    }
    if( !m_LastLayoutIsTemp ) {
      Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/main_frame/layout/default" ), name );
    }
  }
}

void MainFrame::DoAddPluginStatusField( M_Code_Plugin* plugin ) {
  M_Code_StatusBar *sbar = ( M_Code_StatusBar * )GetStatusBar();
  if( !sbar ) {
    return;
  }
  plugin->CreateStatusField( sbar );
  sbar->AdjustFieldsSize();
}

inline void InitToolbar( wxToolBar *tb ) {
  tb->SetInitialSize();
}

ToolbarInfo MainFrame::DoAddPluginToolbar( M_Code_Plugin* plugin ) {
  ToolbarInfo info;
  info.toolbar = Manager::Get()->CreateEmptyToolbar();
  if( plugin->BuildToolBar( info.toolbar ) ) {
    info.priority = plugin->GetToolBarPriority();
    SetToolBar( nullptr );
    InitToolbar( info.toolbar );
    wxMenu* viewToolbars = nullptr;
    GetMenuBar()->FindItem( idViewToolMain, &viewToolbars );
    if( viewToolbars ) {
      wxMenuItem* item = AddPluginInMenus( viewToolbars, plugin, ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )&MainFrame::OnToggleBar, -1, true );
      if( item ) {
        item->Check( true );
        m_PluginsTools[plugin] = info.toolbar;
      }
    }
    const PluginInfo* pluginInfo = Manager::Get()->GetPluginManager()->GetPluginInfo( plugin );
    if( !pluginInfo ) {
      m_Throw( _T( "No plugin info?!?" ) );
    }
    info.paneInfo.Name( pluginInfo->name + _T( "Toolbar" ) ).Caption( pluginInfo->title + _T( " Toolbar" ) );
  } else {
    delete info.toolbar;
    info.toolbar = nullptr;
  }
  return info;
}

void MainFrame::DoAddPlugin( M_Code_Plugin* plugin ) {
  if( plugin->GetType() == ptTool ) {
    AddPluginInPluginsMenu( plugin );
  } else {
    try {
      wxMenuBar* mbar = GetMenuBar();
      plugin->BuildMenu( mbar );
    } catch( M_Code_Exception& e ) {
      e.ShowErrorMessage();
    }
    if( !m_ScanningForPlugins ) {
      const ToolbarInfo &toolbarInfo = DoAddPluginToolbar( plugin );
      if( toolbarInfo.toolbar ) {
        int row = 0;
        const wxAuiPaneInfoArray &panes = m_LayoutManager.GetAllPanes();
        for( size_t ii = 0; ii < panes.GetCount(); ++ii ) {
          const wxAuiPaneInfo &info = panes[ii];
          if( info.IsToolbar() ) {
            row = std::max( row, info.dock_row );
          }
        }
        int minX = 100000, maxX = -100000;
        int position = 0;
        for( size_t ii = 0; ii < panes.GetCount(); ++ii ) {
          const wxAuiPaneInfo &info = panes[ii];
          if( info.IsToolbar() && info.dock_row == row && info.window ) {
            const wxPoint &pt = info.window->GetPosition();
            minX = std::min( minX, pt.x + info.window->GetSize().x );
            maxX = std::max( maxX, pt.x + info.window->GetSize().x );
            position = std::max( position, info.dock_pos );
          }
        }
        if( maxX + toolbarInfo.toolbar->GetSize().x <= GetSize().x ) {
          position++;
        } else {
          row++;
          position = 0;
        }
        wxAuiPaneInfo paneInfo( toolbarInfo.paneInfo );
        m_LayoutManager.AddPane( toolbarInfo.toolbar, paneInfo. ToolbarPane().Top().Row( row ).Position( position ) );
        toolbarInfo.toolbar->Connect( wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED,
                                      wxCommandEventHandler( MainFrame::OnToolBarRightClick ), NULL, this );
        DoUpdateLayout();
      }
    }
    DoAddPluginStatusField( plugin );
  }
}

bool MainFrame::Open( const wxString& filename, bool addToHistory ) {
  wxFileName fn( filename );
  fn.Normalize();
  wxString name = fn.GetFullPath();
  Manager::Get()->GetLogManager()->Log( _T( "打开文件 " ) + name );
  bool ret = OpenGeneric( name, addToHistory );
  return ret;
}

wxString MainFrame::ShowOpenFileDialog( const wxString& caption, const wxString& filter ) {
  wxFileDialog dlg( this, caption, wxEmptyString, wxEmptyString, filter, wxFD_OPEN | compatibility::wxHideReadonly );
  wxString sel;
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    sel = dlg.GetPath();
  }
  return sel;
}

bool MainFrame::OpenGeneric( const wxString& filename, bool addToHistory ) {  //打开通用文件
  if( filename.IsEmpty() ) {
    return false;
  }
  wxFileName fname( filename );
  fname.ClearExt();
  fname.SetExt( FileFilters::M_CODE_EXT );
  switch( FileTypeOf( filename ) ) {
    case ftm_Code_Workspace:
      if( filename == Manager::Get()->GetProjectManager()->GetWorkspace()->GetFilename() ) {
        return true;
      } else {
        if( DoCloseCurrentWorkspace() ) {
          wxBusyCursor wait;
          bool ret = Manager::Get()->GetProjectManager()->LoadWorkspace( filename );
          if( addToHistory ) {
            m_projectsHistory.AddToHistory( Manager::Get()->GetProjectManager()->GetWorkspace()->GetFilename() );
          }
          return ret;
        } else {
          return false;
        }
      }
      break;
    case ftm_Code_Project: {
      M_Code_Project* prj = Manager::Get()->GetProjectManager()->IsOpen( fname.GetFullPath() );
      if( !prj ) {
        wxBusyCursor wait;
        return DoOpenProject( filename, addToHistory );
      } else {
        Manager::Get()->GetProjectManager()->SetProject( prj, false );
        return true;
      }
    }
    case ftSource:
    case ftHeader:
    case ftTemplateSource:
    case ftXMLDocument:
    case ftShell:
    case ftLua:
    case ftText:
    case ftPhp:
    case ftAsp:
    case ftCss:
    case ftConf:
    case ftScript:
    case ftOther:
      return DoOpenFile( filename, addToHistory );
    default: {
      cbMimePlugin* plugin = Manager::Get()->GetPluginManager()->GetMIMEHandlerForFile( filename );
      if( !plugin ) {
        InfoWindow::Tip( _T( "无法处理此文件: " ) + filename, _T( "错误" ), 3500 );
        return false;
      }
      if( plugin->OpenFile( filename ) == 0 ) {
        m_filesHistory.AddToHistory( filename );
        return true;
      }
      return false;
    }
  }
  return true;
}

bool MainFrame::DoOpenProject( const wxString& filename, bool addToHistory ) {
  if( !wxFileExists( filename ) ) {
    InfoWindow::Tip( _T( "项目文件不存在" ), _T( "错误" ), 3500 );
    return false;
  }
  M_Code_Project* prj = Manager::Get()->GetProjectManager()->LoadProject( filename, true );
  if( prj ) {
    if( addToHistory ) {
      m_projectsHistory.AddToHistory( prj->GetFilename() );
    }
    return true;
  }
  return false;
}

bool MainFrame::DoOpenFile( const wxString& filename, bool addToHistory ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->Open( filename );
  if( ed ) {
    ed->Activate();
    if( addToHistory ) {
      m_filesHistory.AddToHistory( ed->GetFilename() );
    }
    return true;
  }
  return false;
}

bool MainFrame::DoCloseCurrentWorkspace() {
  return Manager::Get()->GetProjectManager()->CloseWorkspace();
}

void MainFrame::DoCreateStatusBar() {  //创建状态栏
  wxClientDC dc( this );
  wxFont font = dc.GetFont();
  int h;
  size_t num = 0;
  wxCoord widths[18];
  widths[num++] = -1;
  dc.GetTextExtent( _T( " ---------------- " ), &widths[num++], &h );
  dc.GetTextExtent( _T( " ---------------- " ), &widths[num++], &h );
  dc.GetTextExtent( _T( " 行 0000000, 列 0000000 , pos 00000000 , 已选 0000000 " ), &widths[num++], &h );
  dc.GetTextExtent( _T( " ----------- " ), &widths[num++], &h );  //文件属性
  dc.GetTextExtent( _T( " ----------- " ), &widths[num++], &h );  // 文件状态
  dc.GetTextExtent( _T( " --------------- " ), &widths[num++], &h );  // 文件类型
  dc.GetTextExtent( _T( " ---------------- " ), &widths[num++], &h );  // 配置文件
  wxStatusBar* sb = CreateStatusBar( num );
  if( !sb ) {
    return;
  }
  SetStatusWidths( num, widths );
  wxRect rect;
  if( sb->GetFieldRect( 1, rect ) ) {
    m_pHighlightButton = new wxButton( sb, idHighlightButton, _T( " " ), wxDefaultPosition, wxDefaultSize, wxBORDER_NONE | wxBU_LEFT | wxBU_EXACTFIT );
    const int height = std::max( sb->GetMinHeight(), m_pHighlightButton->GetClientSize().GetHeight() );
    sb->SetMinHeight( height );
  }
}

static void changeButtonLabel( wxButton &button, const wxString &text ) {
  if( text != button.GetLabel() ) {
    button.SetLabel( text );
  }
}

void MainFrame::DoUpdateStatusBar() {  // 更新状态栏
  if( !GetStatusBar() ) {
    return;
  }
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  wxString personality( Manager::Get()->GetPersonalityManager()->GetPersonality() );
  if( ed ) {
    M_Code_StyledTextCtrl * const control = ed->GetControl();
    int panel = 0;
    int pos = control->GetCurrentPos();
    int Selectlen = ed->GetControl()->GetSelectedText().Len();
    wxString msg;
    SetStatusText( ed->GetFilename(), panel++ );
    if( m_pHighlightButton ) {
      EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
      if( colour_set ) {
        changeButtonLabel( *m_pHighlightButton, colour_set->GetLanguageName( ed->GetLanguage() ) );
      } else {
        changeButtonLabel( *m_pHighlightButton, wxEmptyString );
      }
    }
    panel++;
    SetStatusText( ed->GetEncodingName(), panel++ );
    msg.Printf( _T( "行 %d, 列 %d , pos %d , 已选 %d" ), control->GetCurrentLine() + 1, control->GetColumn( pos ) + 1, pos, Selectlen );
    SetStatusText( msg, panel++ );
    SetStatusText( control->GetOvertype() ? _T( "覆盖" ) : _T( "编辑" ), panel++ );
    SetStatusText( ed->GetModified() ? _T( "已修改" ) : _T( "已保存" ), panel++ );
    SetStatusText( control->GetReadOnly() ? _T( " **只读** " ) : _T( "常规文件" ), panel++ );
    SetStatusText( personality, panel++ );
  } else {
    int panel = 0;
    EditorBase *eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
    if( eb ) {
      SetStatusText( eb->GetFilename(), panel++ );
    } else  {
      SetStatusText( _T( "欢迎来到墨世界" ), panel++ );
    }
    if( m_pHighlightButton ) {
      changeButtonLabel( *m_pHighlightButton, wxEmptyString );
    }
    panel++;
    SetStatusText( wxEmptyString, panel++ );
    SetStatusText( wxEmptyString, panel++ );
    SetStatusText( wxEmptyString, panel++ );
    SetStatusText( wxEmptyString, panel++ );
    SetStatusText( wxEmptyString, panel++ );
    SetStatusText( wxEmptyString, panel++ );
    SetStatusText( personality, panel++ );
  }
}

void MainFrame::DoUpdateEditorStyle( M_Code_AuiNotebook* target, const wxString& prefix, long defaultStyle ) {
  if( !target ) {
    return;
  }
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "app" ) );
  target->SetTabCtrlHeight( 1 );
  long nbstyle = 0;
  target->SetArtProvider( new NbStyleGTK() );
  target->SetTabCtrlHeight( -1 );
  nbstyle = defaultStyle;
  if( cfg->ReadBool( _T( "/environment/" ) + prefix + _T( "_tabs_bottom" ) ) ) {
    nbstyle |= wxAUI_NB_BOTTOM;
  }
  if( cfg->ReadBool( _T( "/environment/tabs_list" ) ) ) {
    nbstyle |= wxAUI_NB_WINDOWLIST_BUTTON;
  }
  target->SetWindowStyleFlag( nbstyle );
}

void MainFrame::DoUpdateEditorStyle() {
  long style = wxAUI_NB_TAB_SPLIT | wxAUI_NB_TAB_MOVE | wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_MIDDLE_CLICK_CLOSE;
  style |= wxAUI_NB_CLOSE_BUTTON;  // 统一到右边
  M_Code_AuiNotebook* an = Manager::Get()->GetEditorManager()->GetNotebook();
  DoUpdateEditorStyle( an, _T( "editor" ), style | wxNO_FULL_REPAINT_ON_RESIZE | wxCLIP_CHILDREN );
  if( Manager::Get()->GetConfigManager( _T( "app" ) )->ReadBool( _T( "/environment/hide_editor_tabs" ), false ) ) {
    an->SetTabCtrlHeight( 0 );
  }
  an = m_pInfoPane;
  DoUpdateEditorStyle( an, _T( "infopane" ), style );
  an = m_pPrjManUI->GetNotebook();
  DoUpdateEditorStyle( an, _T( "project" ), wxAUI_NB_SCROLL_BUTTONS | wxAUI_NB_TAB_MOVE );
}

void MainFrame::DoUpdateLayoutColours() {
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "app" ) );
  wxAuiDockArt* art = m_LayoutManager.GetArtProvider();
  m_LayoutManager.SetFlags( ( wxAUI_MGR_DEFAULT | wxAUI_MGR_ALLOW_ACTIVE_PANE | wxAUI_MGR_TRANSPARENT_DRAG | wxAUI_MGR_VENETIAN_BLINDS_HINT ) & ~wxAUI_MGR_TRANSPARENT_HINT );
  art->SetMetric( wxAUI_DOCKART_PANE_BORDER_SIZE, cfg->ReadInt( _T( "/environment/aui/border_size" ), art->GetMetric( wxAUI_DOCKART_PANE_BORDER_SIZE ) ) );
  art->SetMetric( wxAUI_DOCKART_SASH_SIZE, cfg->ReadInt( _T( "/environment/aui/sash_size" ), art->GetMetric( wxAUI_DOCKART_SASH_SIZE ) ) );
  art->SetMetric( wxAUI_DOCKART_CAPTION_SIZE, cfg->ReadInt( _T( "/environment/aui/caption_size" ), art->GetMetric( wxAUI_DOCKART_CAPTION_SIZE ) ) );
  art->SetColour( wxAUI_DOCKART_ACTIVE_CAPTION_COLOUR, cfg->ReadColour( _T( "/environment/aui/active_caption_colour" ), art->GetColour( wxAUI_DOCKART_ACTIVE_CAPTION_COLOUR ) ) );
  art->SetColour( wxAUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR, cfg->ReadColour( _T( "/environment/aui/active_caption_gradient_colour" ), art->GetColour( wxAUI_DOCKART_ACTIVE_CAPTION_GRADIENT_COLOUR ) ) );
  art->SetColour( wxAUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR, cfg->ReadColour( _T( "/environment/aui/active_caption_text_colour" ), art->GetColour( wxAUI_DOCKART_ACTIVE_CAPTION_TEXT_COLOUR ) ) );
  art->SetColour( wxAUI_DOCKART_INACTIVE_CAPTION_COLOUR, cfg->ReadColour( _T( "/environment/aui/inactive_caption_colour" ), art->GetColour( wxAUI_DOCKART_INACTIVE_CAPTION_COLOUR ) ) );
  art->SetColour( wxAUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR, cfg->ReadColour( _T( "/environment/aui/inactive_caption_gradient_colour" ), art->GetColour( wxAUI_DOCKART_INACTIVE_CAPTION_GRADIENT_COLOUR ) ) );
  art->SetColour( wxAUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR, cfg->ReadColour( _T( "/environment/aui/inactive_caption_text_colour" ), art->GetColour( wxAUI_DOCKART_INACTIVE_CAPTION_TEXT_COLOUR ) ) );
  DoUpdateLayout();
}

void MainFrame::DoUpdateLayout() {
  if( !m_StartupDone ) {
    return;
  }
  DoFixToolbarsLayout();
  m_LayoutManager.Update();
}

void MainFrame::DoUpdateAppTitle() {
  EditorBase* ed = Manager::Get()->GetEditorManager() ? Manager::Get()->GetEditorManager()->GetActiveEditor() : nullptr;
  M_Code_Project* prj = nullptr;
  if( ed && ed->IsBuiltinEditor() ) {
    ProjectFile* prjf = ( ( M_Code_Editor* )ed )->GetProjectFile();
    if( prjf ) {
      prj = prjf->GetParentProject();
    }
  } else {
    prj = Manager::Get()->GetProjectManager() ? Manager::Get()->GetProjectManager()->GetActiveProject() : nullptr;
  }
  wxString projname;
  wxString edname;
  wxString fulltitle;
  if( ed || prj ) {
    if( prj ) {
      if( Manager::Get()->GetProjectManager()->GetActiveProject() == prj ) {
        projname = wxString( _T( " [" ) ) + prj->GetTitle() + _T( "]" );
      } else {
        projname = wxString( _T( " (" ) ) + prj->GetTitle() + _T( ")" );
      }
    }
    if( ed ) {
      edname = ed->GetTitle();
    }
    fulltitle = edname + projname;
    if( !fulltitle.IsEmpty() ) {
      fulltitle.Append( _T( " - " ) );
    }
  }
  fulltitle.Append( _T( "m" ) );
  SetTitle( fulltitle );
}

void MainFrame::ShowHideScriptConsole() {
  if( Manager::IsBatchBuild() ) {
    return;
  }
  bool isVis = IsWindowReallyShown( m_pScriptConsole );
  CodeBlocksDockEvent evt( isVis ? cbEVT_HIDE_DOCK_WINDOW : cbEVT_SHOW_DOCK_WINDOW );
  evt.pWindow = m_pScriptConsole;
  Manager::Get()->ProcessEvent( evt );
}

void MainFrame::OnStartHereLink( wxCommandEvent& event ) {
  wxCommandEvent evt;
  evt.SetId( idFileNewProject );
  wxString link = event.GetString();
  if( link.IsSameAs( _T( "CB_CMD_NEW_PROJECT" ) ) ) {
    OnFileNewWhat( evt );
  } else if( link.IsSameAs( _T( "CB_CMD_OPEN_PROJECT" ) ) ) {
    DoOnFileOpen( true );
  } else if( link.StartsWith( _T( "CB_CMD_OPEN_HISTORY_" ) ) ) {
    RecentItemsList *recent;
    recent = link.StartsWith( _T( "CB_CMD_OPEN_HISTORY_PROJECT_" ) ) ? &m_projectsHistory : &m_filesHistory;
    unsigned long item;
    link.AfterLast( _T( '_' ) ).ToULong( &item );
    --item;
    const wxString &filename = recent->GetHistoryFile( item );
    if( !filename.empty() ) {
      if( !OpenGeneric( filename, true ) ) {
        recent->AskToRemoveFileFromHistory( item );
      }
    }
  } else if( link.StartsWith( _T( "CB_CMD_DELETE_HISTORY_" ) ) ) {
    RecentItemsList *recent;
    recent = link.StartsWith( _T( "CB_CMD_DELETE_HISTORY_PROJECT_" ) ) ? &m_projectsHistory : &m_filesHistory;
    unsigned long item;
    link.AfterLast( _T( '_' ) ).ToULong( &item );
    --item;
    recent->AskToRemoveFileFromHistory( item, false );
  } else if( link.IsSameAs( _T( "CB_CMD_TIP_OF_THE_DAY" ) ) ) {
    ShowTips( true );
  }
}

void MainFrame::InitializeRecentFilesHistory() {
  m_filesHistory.Initialize();
  m_projectsHistory.Initialize();
}

void MainFrame::TerminateRecentFilesHistory() {
  m_filesHistory.TerminateHistory();
  m_projectsHistory.TerminateHistory();
}

wxString MainFrame::GetEditorDescription( EditorBase* eb ) {
  wxString descr = wxEmptyString;
  M_Code_Project* prj = NULL;
  if( eb && eb->IsBuiltinEditor() ) {
    ProjectFile* prjf = ( ( M_Code_Editor* )eb )->GetProjectFile();
    if( prjf ) {
      prj = prjf->GetParentProject();
    }
  } else {
    prj = Manager::Get()->GetProjectManager() ? Manager::Get()->GetProjectManager()->GetActiveProject() : nullptr;
  }
  if( prj ) {
    descr = wxString( _T( "Project: " ) ) + _T( "<b>" ) + prj->GetTitle() + _T( "</b>" );
    if( Manager::Get()->GetProjectManager()->GetActiveProject() == prj ) {
      descr += wxString( _T( " (Active)" ) );
    }
    descr += wxString( _T( "<br>" ) );
  }
  if( eb ) {
    descr += eb->GetFilename();
  }
  return descr;
}

void MainFrame::OnPluginsExecuteMenu( wxCommandEvent& event ) {
  wxString pluginName = m_PluginIDsMap[event.GetId()];
  if( !pluginName.IsEmpty() ) {
    Manager::Get()->GetPluginManager()->ExecutePlugin( pluginName );
  } else {
    Manager::Get()->GetLogManager()->LogError( F( _T( "找不到的插件 ID %d" ), event.GetId() ) );
  }
}

void MainFrame::OnFileNewWhat( wxCommandEvent& event ) {
  int id = event.GetId();
  if( id != idFileNewEmpty ) {
    TemplateOutputType tot = totProject;
    if( id == idFileNewProject ) {
      tot = totProject;
    } else if( id == idFileNewTarget ) {
      tot = totTarget;
    } else if( id == idFileNewFile ) {
      tot = totFiles;
    } else if( id == idFileNewCustom ) {
      tot = totCustom;
    } else if( id == idFileNewUser ) {
      tot = totUser;
    } else {
      return;
    }
    wxString filename;
    M_Code_Project* prj = TemplateManager::Get()->New( tot, &filename );
    Manager::Get()->GetEditorManager()->CheckForExternallyModifiedFiles();
    if( !prj && filename.IsEmpty() ) {
      return;
    }
    CodeBlocksEvent evtNew( cbEVT_PROJECT_NEW, 0, prj );
    Manager::Get()->GetPluginManager()->NotifyPlugins( evtNew );
    if( prj ) {
      prj->Save();
      prj->SaveAllFiles();
    }
    if( !filename.IsEmpty() ) {
      if( prj ) {
        m_projectsHistory.AddToHistory( filename );
      } else {
        m_filesHistory.AddToHistory( filename );
      }
    }
    if( prj && tot == totProject ) {
      CodeBlocksEvent evtOpen( cbEVT_PROJECT_OPEN, 0, prj );
      Manager::Get()->GetPluginManager()->NotifyPlugins( evtOpen );
    }
    return;
  }
  M_Code_Project* project = Manager::Get()->GetProjectManager()->GetActiveProject();
  if( project ) {
    wxSetWorkingDirectory( project->GetBasePath() );
  }
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->New();
  if( ed ) {
    ed->GetControl()->SetChangeCollection( Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/margin/use_changebar" ), true ) );
  }
  if( ed && ed->IsOK() ) {
    m_filesHistory.AddToHistory( ed->GetFilename() );
  }
  if( !ed || !project ) {
    return;
  }
  wxString oldname = ed->GetFilename();
  if( msging( _T( "是否要在活动项目中添加此新文件（必须先保存）?" ), _T( "将文件添加到项目" ),
              wxYES_NO | wxICON_QUESTION ) == wxID_YES && ed->SaveAs() && ed->IsOK() ) {
    wxArrayInt targets;
    if( Manager::Get()->GetProjectManager()->AddFileToProject( ed->GetFilename(), project, targets ) != 0 ) {
      ProjectFile* pf = project->GetFileByFilename( ed->GetFilename(), false );
      ed->SetProjectFile( pf );
      m_pPrjManUI->RebuildTree();
    }
  }
  Manager::Get()->GetEditorManager()->CheckForExternallyModifiedFiles();
}

bool MainFrame::OnDropFiles( wxCoord, wxCoord, const wxArrayString& files ) {
  bool success = true;
  wxString foundWorkspace;
  for( unsigned int i = 0; i < files.GetCount(); ++i ) {
    FileType ft = FileTypeOf( files[i] );
    if( ft == ftm_Code_Workspace ) {
      foundWorkspace = files[i];
      break;
    }
  }
  if( !foundWorkspace.IsEmpty() ) {
    success &= OpenGeneric( foundWorkspace );
  } else {
    wxBusyCursor useless;
    wxPaintEvent e;
    ProcessEvent( e );
    for( unsigned int i = 0; i < files.GetCount(); ++i ) {
      success &= OpenGeneric( files[i] );
    }
  }
  return success;
}

void MainFrame::OnFileNew( M_Code_unused wxCommandEvent& event ) {
  wxMenu* popup = nullptr;
  wxMenuBar* bar = GetMenuBar();
  if( !bar ) {
    return;
  }
  bar->FindItem( idFileNewProject, &popup );
  if( popup ) {
    popup = CopyMenu( popup );
    if( popup ) {
      PopupMenu( popup );
      delete popup;
    }
  }
}

void MainFrame::DoOnFileOpen( bool bProject ) {
  wxString Filters = FileFilters::GetFilterString();
  int StoredIndex = FileFilters::GetIndexForFilterAll();
  wxString Path;
  ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "app" ) );
  if( mgr ) {
    if( !bProject ) {
      wxString Filter = mgr->Read( _T( "/file_dialogs/file_new_open/filter" ) );
      if( !Filter.IsEmpty() ) {
        FileFilters::GetFilterIndexFromName( Filters, Filter, StoredIndex );
      }
      Path = mgr->Read( _T( "/file_dialogs/file_new_open/directory" ), Path );
    } else {
      FileFilters::GetFilterIndexFromName( Filters, _T( "项目/工作空间文件" ), StoredIndex );
    }
  }
  wxFileDialog dlg( this, _T( "打开文件" ), Path, wxEmptyString, Filters, wxFD_OPEN | wxFD_MULTIPLE | compatibility::wxHideReadonly );
  dlg.SetFilterIndex( StoredIndex );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    if( mgr && !bProject ) {
      int Index = dlg.GetFilterIndex();
      wxString Filter;
      if( FileFilters::GetFilterNameFromIndex( Filters, Index, Filter ) ) {
        mgr->Write( _T( "/file_dialogs/file_new_open/filter" ), Filter );
      }
      wxString Test = dlg.GetDirectory();
      mgr->Write( _T( "/file_dialogs/file_new_open/directory" ), dlg.GetDirectory() );
    }
    wxArrayString files;
    dlg.GetPaths( files );
    OnDropFiles( 0, 0, files );
  }
}

void MainFrame::OnFileOpen( M_Code_unused wxCommandEvent& event ) {
  DoOnFileOpen( false );
}

void MainFrame::OnFileReopenProject( wxCommandEvent& event ) {
  size_t id = event.GetId() - wxID_CBFILE17;
  wxString fname = m_projectsHistory.GetHistoryFile( id );
  if( !OpenGeneric( fname, true ) ) {
    m_projectsHistory.AskToRemoveFileFromHistory( id );
  }
}

void MainFrame::OnFileOpenRecentProjectClearHistory( M_Code_unused wxCommandEvent& event ) {
  m_projectsHistory.ClearHistory();
}

void MainFrame::OnFileReopen( wxCommandEvent& event ) {
  size_t id = event.GetId() - wxID_CBFILE01;
  wxString fname = m_filesHistory.GetHistoryFile( id );
  if( !OpenGeneric( fname, true ) ) {
    m_filesHistory.AskToRemoveFileFromHistory( id );
  }
}

void MainFrame::OnFileOpenRecentClearHistory( M_Code_unused wxCommandEvent& event ) {
  m_filesHistory.ClearHistory();
}

void MainFrame::OnFileSave( M_Code_unused wxCommandEvent& event ) {
  if( !Manager::Get()->GetEditorManager()->SaveActive() ) {
    wxString msg;
    msg.Printf( _T( "文件 %s 无法保存..." ), Manager::Get()->GetEditorManager()->GetActiveEditor()->GetFilename().wx_str() );
    msging( msg, _T( "保存文件时出错" ), wxICON_ERROR );
  }
  DoUpdateStatusBar();
}

void MainFrame::OnFileSaveAs( M_Code_unused wxCommandEvent& event ) {
  Manager::Get()->GetEditorManager()->SaveActiveAs();
  DoUpdateStatusBar();
}

void MainFrame::OnFileSaveProject( M_Code_unused wxCommandEvent& event ) {
  ProjectManager *prjManager = Manager::Get()->GetProjectManager();
  if( prjManager->SaveActiveProject() ) {
    m_projectsHistory.AddToHistory( prjManager->GetActiveProject()->GetFilename() );
  }
  DoUpdateStatusBar();
  DoUpdateAppTitle();
}

void MainFrame::OnFileSaveProjectAs( M_Code_unused wxCommandEvent& event ) {
  ProjectManager *prjManager = Manager::Get()->GetProjectManager();
  if( prjManager->SaveActiveProjectAs() ) {
    m_projectsHistory.AddToHistory( prjManager->GetActiveProject()->GetFilename() );
  }
  DoUpdateStatusBar();
  DoUpdateAppTitle();
}

void MainFrame::OnFileSaveAll( M_Code_unused wxCommandEvent& event ) {
  Manager::Get()->GetConfigManager( _T( "app" ) )->Flush();
  Manager::Get()->GetEditorManager()->SaveAll();
  ProjectManager *prjManager = Manager::Get()->GetProjectManager();
  prjManager->SaveAllProjects();
  if( prjManager->GetWorkspace()->GetModified() && !prjManager->GetWorkspace()->IsDefault() && prjManager->SaveWorkspace() ) {
    m_projectsHistory.AddToHistory( prjManager->GetWorkspace()->GetFilename() );
  }
  DoUpdateStatusBar();
  DoUpdateAppTitle();
}

void MainFrame::OnFileSaveProjectTemplate( M_Code_unused wxCommandEvent& event ) {
  TemplateManager::Get()->SaveUserTemplate( Manager::Get()->GetProjectManager()->GetActiveProject() );
}

void MainFrame::OnFileCloseProject( M_Code_unused wxCommandEvent& event ) {
  if( !ProjectManager::CanShutdown() || !EditorManager::CanShutdown() ) {
    wxBell();
    return;
  }
  Manager::Get()->GetProjectManager()->CloseActiveProject();
  DoUpdateStatusBar();
}

void MainFrame::OnFileOpenDefWorkspace( M_Code_unused wxCommandEvent& event ) {
  ProjectManager *pman = Manager::Get()->GetProjectManager();
  if( !pman->GetWorkspace()->IsDefault() && !pman->LoadWorkspace() ) {
    msging( _T( "无法打开默认工作区（文件是否存在？）" ), _T( "警告" ), wxICON_WARNING );
  }
}

void MainFrame::OnFileSaveWorkspace( M_Code_unused wxCommandEvent& event ) {
  ProjectManager *pman = Manager::Get()->GetProjectManager();
  if( pman->SaveWorkspace() ) {
    m_projectsHistory.AddToHistory( pman->GetWorkspace()->GetFilename() );
  }
}

void MainFrame::OnFileSaveWorkspaceAs( M_Code_unused wxCommandEvent& event ) {
  ProjectManager *pman = Manager::Get()->GetProjectManager();
  if( pman->SaveWorkspaceAs( _T( "" ) ) ) {
    m_projectsHistory.AddToHistory( pman->GetWorkspace()->GetFilename() );
  }
}

void MainFrame::OnFileCloseWorkspace( M_Code_unused wxCommandEvent& event ) {
  DoCloseCurrentWorkspace();
}

void MainFrame::OnFileClose( M_Code_unused wxCommandEvent& event ) {
  Manager::Get()->GetEditorManager()->CloseActive();
  DoUpdateStatusBar();
  Refresh();
}

void MainFrame::OnFileCloseAll( M_Code_unused wxCommandEvent& event ) {
  Manager::Get()->GetEditorManager()->CloseAll();
  DoUpdateStatusBar();
}

void MainFrame::OnFileNext( M_Code_unused wxCommandEvent& event ) {
  Manager::Get()->GetEditorManager()->ActivateNext();
  DoUpdateStatusBar();
}

void MainFrame::OnFilePrev( M_Code_unused wxCommandEvent& event ) {
  Manager::Get()->GetEditorManager()->ActivatePrevious();
  DoUpdateStatusBar();
}

void MainFrame::OnFileQuit( M_Code_unused wxCommandEvent& event ) {
  Close( );
}

void MainFrame::OnEraseBackground( wxEraseEvent& event ) {
  event.Skip();
}

void MainFrame::OnSize( wxSizeEvent& event ) {
  if( m_pHighlightButton ) {
    wxRect rect;
    if( GetStatusBar()->GetFieldRect( 1, rect ) ) {
      m_pHighlightButton->SetPosition( rect.GetPosition() );
      m_pHighlightButton->SetSize( rect.GetSize() );
    }
  }
  event.Skip();
}

void MainFrame::OnApplicationClose( wxCloseEvent& event ) {
  if( m_InitiatedShutdown ) {
    return;
  }
  CodeBlocksEvent evt( cbEVT_APP_START_SHUTDOWN );
  Manager::Get()->ProcessEvent( evt );
  Manager::Yield();
  m_InitiatedShutdown = true;
  Manager::BlockYields( true );
  {
    bool hasRunning = cbHasRunningCompilers( Manager::Get()->GetPluginManager() );
    if( hasRunning ) {
      int result = msging( _T( "当前正在编译 停止编译并退出?" ), _T( "问题" ), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION, this );
      if( result == wxID_YES ) {
        cbStopRunningCompilers( Manager::Get()->GetPluginManager() );
      } else {
        event.Veto();
        wxBell();
        m_InitiatedShutdown = false;
        Manager::BlockYields( false );
        return;
      }
    }
  }
  if( !ProjectManager::CanShutdown() || !EditorManager::CanShutdown() ) {
    event.Veto();
    wxBell();
    m_InitiatedShutdown = false;
    Manager::BlockYields( false );
    return;
  }
  if( !DoCloseCurrentWorkspace() ) {
    event.Veto();
    m_InitiatedShutdown = false;
    Manager::BlockYields( false );
    return;
  }
  Manager::SetAppShuttingDown( true );
  Manager::Get()->GetLogManager()->Log( _T( "取消初始化插件..." ) );
  CodeBlocksEvent evtShutdown( cbEVT_APP_START_SHUTDOWN );
  Manager::Get()->ProcessEvent( evtShutdown );
  Manager::Yield();
  if( !Manager::IsBatchBuild() ) {
    SaveWindowState();
  }
  if( m_pPrjManUI->GetNotebook() ) {
    m_LayoutManager.DetachPane( m_pPrjManUI->GetNotebook() );
  }
  m_LayoutManager.DetachPane( m_pInfoPane );
  m_LayoutManager.DetachPane( Manager::Get()->GetEditorManager()->GetNotebook() );
  m_LayoutManager.UnInit();
  TerminateRecentFilesHistory();
  while( GetEventHandler() != this ) {
    PopEventHandler( false );
  }
  Hide();
  if( !Manager::IsBatchBuild() ) {
    m_pInfoPane->Destroy();
    m_pInfoPane = nullptr;
  }
  PluginToolbarsMap::iterator it;
  for( it = m_PluginsTools.begin(); it != m_PluginsTools.end(); ++it ) {
    wxToolBar* toolbar = it->second;
    if( toolbar ) {
      bool result = toolbar->Disconnect( wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEventHandler( MainFrame::OnToolBarRightClick ) );
      cbAssert( result );
    }
  }
  Manager::Shutdown();
  Destroy();
}

void MainFrame::OnEditSwapHeaderSource( M_Code_unused wxCommandEvent& event ) {
  Manager::Get()->GetEditorManager()->SwapActiveHeaderSource();
  DoUpdateStatusBar();
}

void MainFrame::OnEditGotoMatchingBrace( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GotoMatchingBrace();
  }
}

void MainFrame::OnEditBookmarksToggle( M_Code_unused wxCommandEvent& event ) {
  EditorBase* ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( ed && ed->IsBuiltinEditor() ) {
    static_cast<M_Code_Editor*>( ed )->ToggleBookmark();
  }
}

void MainFrame::OnEditBookmarksNext( M_Code_unused wxCommandEvent& event ) {
  EditorBase* ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( ed && ed->IsBuiltinEditor() ) {
    static_cast<M_Code_Editor*>( ed )->GotoNextBookmark();
  }
}

void MainFrame::OnEditBookmarksPrevious( M_Code_unused wxCommandEvent& event ) {
  EditorBase* ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( ed && ed->IsBuiltinEditor() ) {
    static_cast<M_Code_Editor*>( ed )->GotoPreviousBookmark();
  }
}

void MainFrame::OnEditBookmarksClearAll( wxCommandEvent& event ) {
  EditorBase* ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( ed && ed->IsBuiltinEditor() ) {
    static_cast<M_Code_Editor*>( ed )->ClearAllBookmarks();
  }
}

void MainFrame::OnEditUndo( M_Code_unused wxCommandEvent& event ) {
  EditorBase* ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( ed && ed->CanUndo() ) {
    M_Code_Editor* cbEd = Manager::Get()->GetEditorManager()->GetBuiltinEditor( ed );
    if( cbEd && cbEd->GetControl()->AutoCompActive() ) {
      cbEd->GetControl()->AutoCompCancel();
    }
    ed->Undo();
  }
}

void MainFrame::OnEditRedo( M_Code_unused wxCommandEvent& event ) {
  EditorBase* ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( ed ) {
    ed->Redo();
  }
}

void MainFrame::OnEditClearHistory( M_Code_unused wxCommandEvent& event ) {
  EditorBase* ed = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( ed ) {
    ed->ClearHistory();
  }
}

void MainFrame::OnEditParaUp( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->ParaUp();
  }
}

void MainFrame::OnEditParaUpExtend( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->ParaUpExtend();
  }
}

void MainFrame::OnEditParaDown( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->ParaDown();
  }
}

void MainFrame::OnEditParaDownExtend( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->ParaDownExtend();
  }
}

void MainFrame::OnEditWordPartLeft( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->WordPartLeft();
  }
}

void MainFrame::OnEditWordPartLeftExtend( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->WordPartLeftExtend();
  }
}

void MainFrame::OnEditWordPartRight( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->WordPartRight();
  }
}

void MainFrame::OnEditWordPartRightExtend( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->WordPartRightExtend();
  }
}

void MainFrame::OnEditZoomIn( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->ZoomIn();
  }
}

void MainFrame::OnEditZoomOut( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->ZoomOut();
  }
}

void MainFrame::OnEditZoomReset( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->SetZoom( 0 );
  }
}

void MainFrame::OnEditLineCut( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->LineCut();
  }
}

void MainFrame::OnEditLineDelete( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->LineDelete();
  }
}

void MainFrame::OnEditLineDuplicate( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->LineDuplicate();
  }
}

void MainFrame::OnEditLineTranspose( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->LineTranspose();
  }
}

void MainFrame::OnEditLineCopy( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->LineCopy();
  }
}

void MainFrame::OnEditLinePaste( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->BeginUndoAction();
    int pos = ed->GetControl()->GetCurrentPos();
    int line = ed->GetControl()->LineFromPosition( pos );
    ed->GetControl()->GotoLine( line );
    int column = pos - ed->GetControl()->GetCurrentPos();
    ed->GetControl()->Paste();
    pos = ed->GetControl()->GetCurrentPos();
    ed->GetControl()->GotoPos( pos + column );
    ed->GetControl()->EndUndoAction();
  }
}

void MainFrame::OnEditLineMove( wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  if( !stc || ( stc->GetSelections() > 1 ) ) {
    return;
  }
  if( stc->GetLineCount() < 3 ) {
    return;
  }
  int startPos = stc->PositionFromLine( stc->LineFromPosition( stc->GetSelectionStart() ) );
  int endPos = stc->LineFromPosition( stc->GetSelectionEnd() );
  if( stc->GetSelectionEnd() == stc->PositionFromLine( endPos )
      && stc->PositionFromLine( endPos ) != stc->GetLineEndPosition( endPos )
      && endPos > stc->LineFromPosition( startPos ) ) {
    --endPos;
  }
  const bool isLastLine = ( endPos == stc->GetLineCount() - 1 );
  endPos = ( isLastLine ? stc->GetLineEndPosition( endPos ) : stc->PositionFromLine( endPos + 1 ) - 1 );
  if( event.GetId() == idEditLineUp ) {
    if( stc->LineFromPosition( startPos ) < 1 ) {
      return;
    }
    stc->BeginUndoAction();
    const int offset = ( isLastLine ? startPos - stc->GetLineEndPosition( stc->LineFromPosition( startPos ) - 1 ) : 0 );
    const int lineLength = startPos - stc->PositionFromLine( stc->LineFromPosition( startPos ) - 1 );
    const wxString line = stc->GetTextRange( startPos - lineLength - offset, startPos - offset );
    stc->InsertText( endPos + ( isLastLine ? 0 : 1 ), line );
    stc->DeleteRange( startPos - lineLength, lineLength );
    startPos -= lineLength;
    endPos -= lineLength;
    stc->EndUndoAction();
  } else {
    if( isLastLine ) {
      return;
    }
    stc->BeginUndoAction();
    const int lineLength = stc->PositionFromLine( stc->LineFromPosition( endPos + 1 ) + 1 ) - endPos - 1;
    const wxString line = stc->GetTextRange( endPos + 1, endPos + 1 + lineLength );
    stc->InsertText( startPos, line );
    startPos += lineLength;
    endPos += lineLength;
    stc->DeleteRange( endPos + 1, lineLength );
    stc->EndUndoAction();
  }
  stc->SetSelectionVoid( startPos, endPos );
}

void MainFrame::OnEditUpperCase( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->UpperCase();
  }
}

void MainFrame::OnEditLowerCase( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GetControl()->LowerCase();
  }
}

void MainFrame::OnEditInsertNewLine( wxCommandEvent& event ) {
  OnEditGotoLineEnd( event );
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    M_Code_StyledTextCtrl* stc = ed->GetControl();
    if( stc->AutoCompActive() ) {
      stc->AutoCompCancel();
    }
    stc->NewLine();
  }
}

void MainFrame::OnEditGotoLineEnd( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    M_Code_StyledTextCtrl* stc = ed->GetControl();
    if( stc->AutoCompActive() ) {
      stc->AutoCompCancel();
    }
    stc->LineEnd();
  }
}

static void InsertNewLine( bool below ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    M_Code_StyledTextCtrl* stc = ed->GetControl();
    stc->BeginUndoAction();
    if( stc->AutoCompActive() ) {
      stc->AutoCompCancel();
    }
    if( below ) {
      stc->LineEndDisplay();
      int pos = stc->GetCurrentPos();
      stc->InsertText( pos, _T( "\n" ) );
      stc->LineDown();
    } else {
      stc->HomeDisplay();
      int pos = stc->GetCurrentPos();
      stc->InsertText( pos, _T( "\n" ) );
      stc->EndUndoAction();
    }
    stc->EndUndoAction();
  }
}

void MainFrame::OnEditInsertNewLineBelow( wxCommandEvent& event ) {
  InsertNewLine( true );
}

void MainFrame::OnEditInsertNewLineAbove( wxCommandEvent& event ) {
  InsertNewLine( false );
}

void MainFrame::OnEditSelectAll( M_Code_unused wxCommandEvent& event ) {
  EditorBase* eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( eb ) {
    eb->SelectAll();
  }
}

namespace {
  struct EditorSelection {
    long caret, anchor;
    bool Empty() const { return caret == anchor; }
    bool IsReversed() const { return caret < anchor; }
    long GetStart() const { return std::min( caret, anchor ); }
    long GetEnd() const { return std::max( caret, anchor ); }
    bool Contains( const EditorSelection &selection ) const {
      return !( GetEnd() < selection.GetStart() || GetStart() > selection.GetEnd() );
    }
  };

  bool SelectNext( M_Code_StyledTextCtrl *control, const wxString &selectedText, long selectionEnd, bool reversed ) {
    int flag = wxSCI_FIND_MATCHCASE;
    if( selectedText.find_first_of( _T( ";:\"'`~@#$%^,-+*/\\=|!?&*(){}[]" ) ) == wxString::npos ) {
      flag |= wxSCI_FIND_WHOLEWORD;
    }
    int lengthFound = 0;
    int eof = control->GetLength();
    int pos = control->FindText( selectionEnd, eof, selectedText, flag, &lengthFound );
    if( pos != wxSCI_INVALID_POSITION ) {
      control->SetAdditionalSelectionTyping( true );
      control->IndicatorClearRange( pos, lengthFound );
      if( reversed ) {
        control->AddSelection( pos, pos + lengthFound );
      } else {
        control->AddSelection( pos + lengthFound, pos );
      }
      control->MakeNearbyLinesVisible( control->LineFromPosition( pos ) );
      return true;
    } else {
      InfoWindow::Tip( _T( "提示" ), _T( "选择下一个事件" ) );
      return false;
    }
  }

  bool GetSelectionInEditor( EditorSelection &selection, M_Code_StyledTextCtrl *control ) {
    int main = control->GetMainSelection();
    int count = control->GetSelections();
    if( main >= 0 && main < count ) {
      selection.caret = control->GetSelectionNCaret( main );
      selection.anchor = control->GetSelectionNAnchor( main );
      return true;
    } else {
      return false;
    }
  }

}

void MainFrame::OnEditSelectNext( M_Code_unused wxCommandEvent& event ) {
  EditorBase* eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( !eb || !eb->IsBuiltinEditor() ) {
    return;
  }
  M_Code_StyledTextCtrl *control = static_cast<M_Code_Editor*>( eb )->GetControl();
  EditorSelection selection;
  if( !GetSelectionInEditor( selection, control ) ) {
    return;
  }
  if( !selection.Empty() ) {
    const wxString &selectedText( control->GetTextRange( selection.GetStart(), selection.GetEnd() ) );
    SelectNext( control, selectedText, selection.GetEnd(), selection.IsReversed() );
  } else {
    const int start = control->WordStartPosition( selection.caret, true );
    const int end = control->WordEndPosition( selection.caret, true );
    control->SetSelection( start, end );
  }
}

void MainFrame::OnEditSelectNextSkip( M_Code_unused wxCommandEvent& event ) {
  EditorBase* eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
  if( !eb || !eb->IsBuiltinEditor() ) {
    return;
  }
  M_Code_StyledTextCtrl *control = static_cast<M_Code_Editor*>( eb )->GetControl();
  EditorSelection selection;
  if( !GetSelectionInEditor( selection, control ) ) {
    return;
  }
  ConfigManager *cfgEditor = Manager::Get()->GetConfigManager( _T( "editor" ) );
  bool highlightOccurrences = cfgEditor->ReadBool( _T( "/highlight_occurrence/enabled" ), true );
  const wxString &selectedText( control->GetTextRange( selection.GetStart(), selection.GetEnd() ) );
  if( !SelectNext( control, selectedText, selection.GetEnd(), selection.IsReversed() ) ) {
    return;
  }
  typedef std::vector<EditorSelection> Selections;
  Selections selections;
  int count = control->GetSelections();
  for( int ii = 0; ii < count; ++ii ) {
    EditorSelection item;
    item.caret = control->GetSelectionNCaret( ii );
    item.anchor = control->GetSelectionNAnchor( ii );
    if( !item.Contains( selection ) ) {
      selections.push_back( item );
    } else if( highlightOccurrences ) {
      control->IndicatorFillRange( item.GetStart(), item.GetEnd() );
    }
  }
  control->ClearSelections();
  Selections::const_iterator it = selections.begin();
  int index = 0;
  if( it != selections.end() && control->GetSelections() > 0 ) {
    control->SetSelectionNAnchor( index, it->anchor );
    control->SetSelectionNCaret( index, it->caret );
    ++index;
    ++it;
  }
  for( ; it != selections.end(); ++it ) {
    control->AddSelection( it->caret, it->anchor );
    ++index;
  }
}


void MainFrame::OnEditCommentSelected( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  if( !stc ) {
    return;
  }
  EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
  if( !colour_set ) {
    return;
  }
  CommentToken comment = colour_set->GetCommentToken( ed->GetLanguage() );
  if( comment.lineComment == wxEmptyString && comment.streamCommentStart == wxEmptyString ) {
    return;
  }
  stc->BeginUndoAction();
  if( wxSCI_INVALID_POSITION != stc->GetSelectionStart() ) {
    int startLine = stc->LineFromPosition( stc->GetSelectionStart() );
    int endLine = stc->LineFromPosition( stc->GetSelectionEnd() );
    int curLine = startLine;
    if( startLine != endLine && stc->GetColumn( stc->GetSelectionEnd() ) == 0 ) {
      --endLine;
    }
    while( curLine <= endLine ) {
      if( comment.lineComment != wxEmptyString ) {
        stc->InsertText( stc->PositionFromLine( curLine ), comment.lineComment );
      } else {
        stc->InsertText( stc->PositionFromLine( curLine ), comment.streamCommentStart );
        stc->InsertText( stc->GetLineEndPosition( curLine ), comment.streamCommentEnd );
      }
      ++curLine;
    }
    stc->SetSelectionVoid( stc->PositionFromLine( startLine ), stc->PositionFromLine( endLine ) + stc->LineLength( endLine ) );
  }
  stc->EndUndoAction();
}


void MainFrame::OnEditUncommentSelected( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  if( !stc ) {
    return;
  }
  EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
  if( !colour_set ) {
    return;
  }
  CommentToken comment = colour_set->GetCommentToken( ed->GetLanguage() );
  if( comment.lineComment == wxEmptyString && comment.streamCommentStart == wxEmptyString ) {
    return;
  }
  stc->BeginUndoAction();
  if( wxSCI_INVALID_POSITION != stc->GetSelectionStart() ) {
    int startLine = stc->LineFromPosition( stc->GetSelectionStart() );
    int endLine = stc->LineFromPosition( stc->GetSelectionEnd() );
    int curLine = startLine;
    if( startLine != endLine && stc->GetColumn( stc->GetSelectionEnd() ) == 0 ) {
      --endLine;
    }
    while( curLine <= endLine ) {
      wxString strLine = stc->GetLine( curLine );
      bool startsWithComment;
      bool endsWithComment;
      startsWithComment = strLine.Strip( wxString::leading ).StartsWith( comment.lineComment );
      if( startsWithComment ) {
        int Pos = strLine.Find( comment.lineComment );
        int start = stc->PositionFromLine( curLine ) + Pos;
        int end = start + comment.lineComment.Length();
        stc->SetTargetStart( start );
        stc->SetTargetEnd( end );
        stc->ReplaceTarget( wxEmptyString );
      }
      startsWithComment = strLine.Strip( wxString::leading ).StartsWith( comment.streamCommentStart );
      endsWithComment = strLine.Strip( wxString::trailing ).EndsWith( comment.streamCommentEnd );
      if( startsWithComment && endsWithComment ) {
        int Pos;
        int start;
        int end;
        Pos = strLine.Find( comment.streamCommentStart );
        start = stc->PositionFromLine( curLine ) + Pos;
        end = start + comment.streamCommentStart.Length();
        stc->SetTargetStart( start );
        stc->SetTargetEnd( end );
        stc->ReplaceTarget( wxEmptyString );
        Pos = strLine.rfind( comment.streamCommentEnd, strLine.npos ) - comment.streamCommentStart.Length();
        start = stc->PositionFromLine( curLine ) + Pos;
        end = start + comment.streamCommentEnd.Length();
        stc->SetTargetStart( start );
        stc->SetTargetEnd( end );
        stc->ReplaceTarget( wxEmptyString );
      }
      ++curLine;
    }
    stc->SetSelectionVoid( stc->PositionFromLine( startLine ), stc->PositionFromLine( endLine ) + stc->LineLength( endLine ) );
  }
  stc->EndUndoAction();
}

void MainFrame::OnEditToggleCommentSelected( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  if( !stc ) {
    return;
  }
  EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
  if( !colour_set ) {
    return;
  }
  wxString comment = colour_set->GetCommentToken( ed->GetLanguage() ).lineComment;
  if( comment == wxEmptyString ) {
    return;
  }
  stc->BeginUndoAction();
  if( wxSCI_INVALID_POSITION != stc->GetSelectionStart() ) {
    int startLine = stc->LineFromPosition( stc->GetSelectionStart() );
    int endLine = stc->LineFromPosition( stc->GetSelectionEnd() );
    int curLine = startLine;
    if( startLine != endLine && stc->GetColumn( stc->GetSelectionEnd() ) == 0 ) {
      --endLine;
    }
    bool doComment = false;
    while( curLine <= endLine ) {
      wxString strLine = stc->GetLine( curLine );
      int commentPos = strLine.Strip( wxString::leading ).Find( comment );
      if( commentPos != 0 ) {
        doComment = true;
        break;
      }
      ++curLine;
    }
    curLine = startLine;
    while( curLine <= endLine ) {
      if( doComment ) {
        stc->InsertText( stc->PositionFromLine( curLine ), comment );
      } else {
        wxString strLine = stc->GetLine( curLine );
        int Pos = strLine.Find( comment );
        int start = stc->PositionFromLine( curLine ) + Pos;
        int end = start + comment.Length();
        stc->SetTargetStart( start );
        stc->SetTargetEnd( end );
        stc->ReplaceTarget( wxEmptyString );
      }
      ++curLine;
    }
    stc->SetSelectionVoid( stc->PositionFromLine( startLine ), stc->PositionFromLine( endLine ) + stc->LineLength( endLine ) );
  }
  stc->EndUndoAction();
}

void MainFrame::OnEditStreamCommentSelected( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  if( !stc ) {
    return;
  }
  EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
  if( !colour_set ) {
    return;
  }
  CommentToken comment = colour_set->GetCommentToken( ed->GetLanguage() );
  if( comment.streamCommentStart == wxEmptyString ) {
    return;
  }
  stc->BeginUndoAction();
  if( wxSCI_INVALID_POSITION != stc->GetSelectionStart() ) {
    int startPos = stc->GetSelectionStart();
    int endPos = stc->GetSelectionEnd();
    if( startPos == endPos ) {
      startPos = stc->WordStartPosition( stc->GetCurrentPos(), true );
      endPos = stc->WordEndPosition( stc->GetCurrentPos(), true );
      if( startPos == endPos ) {
        startPos = stc->PositionFromLine( stc->LineFromPosition( startPos ) );
        endPos = stc->GetLineEndPosition( stc->LineFromPosition( startPos ) );
      }
    } else {
      if( stc->GetColumn( stc->GetSelectionEnd() ) == 0 ) {
        --endPos;
      }
    }
    int p1 = startPos - 1;
    while( stc->GetCharAt( p1 ) == _T( ' ' ) && p1 > 0 ) {
      --p1;
    }
    p1 -= 1;
    int p2 = endPos;
    while( stc->GetCharAt( p2 ) == _T( ' ' ) && p2 < stc->GetLength() ) {
      ++p2;
    }
    const wxString start = stc->GetTextRange( p1, p1 + comment.streamCommentStart.Length() );
    const wxString end = stc->GetTextRange( p2, p2 + comment.streamCommentEnd.Length() );
    if( start == comment.streamCommentStart && end == comment.streamCommentEnd ) {
      stc->SetTargetStart( p1 );
      stc->SetTargetEnd( p2 + 2 );
      wxString target = stc->GetTextRange( p1 + 2, p2 );
      stc->ReplaceTarget( target );
      stc->GotoPos( p1 + target.Length() );
    } else {
      stc->InsertText( startPos, comment.streamCommentStart );
      startPos += comment.streamCommentStart.Length();
      endPos += comment.streamCommentStart.Length();
      stc->InsertText( endPos, comment.streamCommentEnd );
      stc->SetSelectionVoid( startPos, endPos );
    }
  }
  stc->EndUndoAction();
}

void MainFrame::OnEditBoxCommentSelected( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  M_Code_StyledTextCtrl* stc = ed->GetControl();
  if( !stc ) {
    return;
  }
  EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
  if( !colour_set ) {
    return;
  }
  CommentToken comment = colour_set->GetCommentToken( ed->GetLanguage() );
  if( comment.boxCommentStart == wxEmptyString ) {
    return;
  }
  stc->BeginUndoAction();
  if( wxSCI_INVALID_POSITION != stc->GetSelectionStart() ) {
    int startLine = stc->LineFromPosition( stc->GetSelectionStart() );
    int endLine = stc->LineFromPosition( stc->GetSelectionEnd() );
    int curLine = startLine;
    if( startLine != endLine && stc->GetColumn( stc->GetSelectionEnd() ) == 0 ) {
      --endLine;
    }
    if( startLine == endLine ) {
      stc->InsertText( stc->PositionFromLine( curLine ), comment.streamCommentStart );
      stc->InsertText( stc->GetLineEndPosition( curLine ), comment.streamCommentEnd );
    } else {
      stc->InsertText( stc->PositionFromLine( curLine ), comment.boxCommentStart );
      ++curLine;
      while( curLine <= endLine ) {
        stc->InsertText( stc->PositionFromLine( curLine ), comment.boxCommentMid );
        ++curLine;
      }
      stc->InsertText( stc->PositionFromLine( curLine ), comment.boxCommentEnd + _T( "\n" ) );
    }
    stc->SetSelectionVoid( stc->PositionFromLine( startLine ), stc->PositionFromLine( endLine ) + stc->LineLength( endLine ) );
  }
  stc->EndUndoAction();
}

void MainFrame::OnEditShowCallTip( M_Code_unused wxCommandEvent& event ) {
  CodeBlocksEvent evt( cbEVT_SHOW_CALL_TIP );
  Manager::Get()->ProcessEvent( evt );
}

void MainFrame::OnEditCompleteCode( M_Code_unused wxCommandEvent& event ) {
  CodeBlocksEvent evt( cbEVT_COMPLETE_CODE );
  Manager::Get()->ProcessEvent( evt );
}

void MainFrame::OnEditHighlightMode( wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
  if( !colour_set ) {
    return;
  }
  HighlightLanguage lang = colour_set->GetHighlightLanguage( _T( "" ) );
  if( event.GetId() != idEditHighlightModeText ) {
    wxMenu* hl = nullptr;
    GetMenuBar()->FindItem( idEditHighlightModeText, &hl );
    if( hl ) {
      wxMenuItem* item = hl->FindItem( event.GetId() );
      if( item ) {
        lang = colour_set->GetHighlightLanguage( item->GetLabel() );
      }
    }
  }
  if( m_pHighlightButton ) {
    changeButtonLabel( *m_pHighlightButton, colour_set->GetLanguageName( lang ) );
  }
  ed->SetLanguage( lang );
  Manager::Get()->GetCCManager()->NotifyPluginStatus();
}

void MainFrame::OnEditFoldAll( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->FoldAll();
  }
}

void MainFrame::OnEditUnfoldAll( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->UnfoldAll();
  }
}

void MainFrame::OnEditToggleAllFolds( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->ToggleAllFolds();
  }
}

void MainFrame::OnEditFoldBlock( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->FoldBlockFromLine();
  }
}

void MainFrame::OnEditUnfoldBlock( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->UnfoldBlockFromLine();
  }
}

void MainFrame::OnEditToggleFoldBlock( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->ToggleFoldBlockFromLine();
  }
}

void MainFrame::OnEditEncoding( wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  if( event.GetId() == idEditEncodingUseBom ) {
    ed->SetUseBom( !ed->GetUseBom() );
    return;
  }
  wxFontEncoding encoding = wxFONTENCODING_SYSTEM;
  if( event.GetId() == idEditEncodingDefault ) {
    encoding = wxFONTENCODING_SYSTEM;
  } else if( event.GetId() == idEditEncodingAscii ) {
    encoding = wxFONTENCODING_ISO8859_1;
  } else if( event.GetId() == idEditEncodingUtf7 ) {
    encoding = wxFONTENCODING_UTF7;
  } else if( event.GetId() == idEditEncodingUtf8 ) {
    encoding = wxFONTENCODING_UTF8;
  } else if( event.GetId() == idEditEncodingUtf16 ) {
    encoding = wxFONTENCODING_UTF16;
  } else if( event.GetId() == idEditEncodingUtf32 ) {
    encoding = wxFONTENCODING_UTF32;
  } else if( event.GetId() == idEditEncodingUnicode ) {
    encoding = wxFONTENCODING_UNICODE;
  } else if( event.GetId() == idEditEncodingUnicode16BE ) {
    encoding = wxFONTENCODING_UTF16BE;
  } else if( event.GetId() == idEditEncodingUnicode16LE ) {
    encoding = wxFONTENCODING_UTF16LE;
  } else if( event.GetId() == idEditEncodingUnicode32BE ) {
    encoding = wxFONTENCODING_UTF32BE;
  } else if( event.GetId() == idEditEncodingUnicode32LE ) {
    encoding = wxFONTENCODING_UTF32LE;
  }
  ed->SetEncoding( encoding );
}

void MainFrame::OnViewLayout( wxCommandEvent& event ) {
  LoadViewLayout( m_PluginIDsMap[event.GetId()] );
}

void MainFrame::OnViewLayoutSave( M_Code_unused wxCommandEvent& event ) {
  wxString def = m_LastLayoutName;
  if( def.empty() ) {
    def = Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "/main_frame/layout/default" ) );
  }
  wxString name = cbGetTextFromUser( _T( "输入此视图的名称" ), _T( "保存当前视图" ), def );
  if( !name.IsEmpty() ) {
    DoFixToolbarsLayout();
    SaveViewLayout( name, m_LayoutManager.SavePerspective(), m_pInfoPane->SaveTabOrder(), true );
  }
}

void MainFrame::OnViewLayoutDelete( M_Code_unused wxCommandEvent& event ) {
  if( m_LastLayoutName == gDefaultLayout ) {
    if( msging( _T( "无法删除默认视图 但它始终可以恢复到预先定义的状态 现在要还原它吗?" ), _T( "审查" ), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT ) == wxID_YES ) {
      m_LayoutViews[gDefaultLayout] = gDefaultLayoutData;
      m_LayoutMessagePane[gDefaultLayout] = gDefaultMessagePaneLayoutData;
      LoadViewLayout( gDefaultLayout );
    }
    return;
  }
  if( m_LastLayoutName == gMinimalLayout ) {
    if( msging( _T( "无法删除mini布局 但它始终可以恢复到预先定义的状态 现在要还原它吗?" ), _T( "审查" ), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT ) == wxID_YES ) {
      wxString tempLayout = m_PreviousLayoutName;
      m_LayoutViews[gMinimalLayout] = gMinimalLayoutData;
      m_LayoutMessagePane[gMinimalLayout] = gMinimalMessagePaneLayoutData;
      LoadViewLayout( gMinimalLayout );
      m_PreviousLayoutName = tempLayout;
    }
    return;
  }
  if( msging( F( _T( "是否确实要删除视图 '%s'?" ), m_LastLayoutName.wx_str() ), _T( "审查" ), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT ) == wxID_YES ) {
    LayoutViewsMap::iterator it = m_LayoutViews.find( m_LastLayoutName );
    if( it != m_LayoutViews.end() ) {
      m_LayoutViews.erase( it );
    }
    it = m_LayoutMessagePane.find( m_LastLayoutName );
    if( it != m_LayoutMessagePane.end() ) {
      m_LayoutMessagePane.erase( it );
    }
    wxMenu* viewLayouts = nullptr;
    GetMenuBar()->FindItem( idViewLayoutSave, &viewLayouts );
    if( viewLayouts ) {
      int id = viewLayouts->FindItem( m_LastLayoutName );
      if( id != wxNOT_FOUND ) {
        viewLayouts->Delete( id );
      }
      PluginIDsMap::iterator it2 = m_PluginIDsMap.find( id );
      if( it2 != m_PluginIDsMap.end() ) {
        m_PluginIDsMap.erase( it2 );
      }
    }
    msging( F( _T( "视图 '%s' 删除.\n现在将恢复视图 '%s'..." ), m_LastLayoutName.wx_str(), gDefaultLayout.wx_str() ), _T( "消息" ), wxICON_INFORMATION );
    m_LastLayoutName = gDefaultLayout;
    LoadViewLayout( gDefaultLayout );
  }
}

void MainFrame::OnNotebookDoubleClick( M_Code_unused CodeBlocksEvent& event ) {
  if( m_LastLayoutName == gMinimalLayout ) {
    LoadViewLayout( m_PreviousLayoutName.IsEmpty() ? Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "/environment/view/layout_to_toggle" ), gDefaultLayout ) :
                    m_PreviousLayoutName );
  } else {
    ConfigManager *cfg = Manager::Get()->GetConfigManager( _T( "app" ) );
    if( cfg->ReadBool( _T( "/environment/view/dbl_clk_maximize" ), true ) ) {
      LoadViewLayout( gMinimalLayout );
    }
  }
}

void MainFrame::OnViewScriptConsole( M_Code_unused wxCommandEvent& event ) {
  ShowHideScriptConsole();
}

void MainFrame::OnViewHideEditorTabs( M_Code_unused wxCommandEvent& event ) {
  M_Code_AuiNotebook* nb = Manager::Get()->GetEditorManager()->GetNotebook();
  if( nb ) {
    bool hide_editor_tabs = nb->GetTabCtrlHeight() > 0;
    if( hide_editor_tabs ) {
      nb->SetTabCtrlHeight( 0 );
    } else {
      nb->SetTabCtrlHeight( -1 );
    }
    Manager::Get()->GetConfigManager( _T( "app" ) )->Write( _T( "/environment/hide_editor_tabs" ), hide_editor_tabs );
  }
}

void MainFrame::OnSearchFind( wxCommandEvent& event ) {
  bool bDoMultipleFiles = ( event.GetId() == idSearchFindInFiles );
  if( !bDoMultipleFiles ) {
    bDoMultipleFiles = !Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  }
  m_findReplace.ShowFindDialog( false, bDoMultipleFiles );
}

void MainFrame::OnSearchFindNext( wxCommandEvent& event ) {
  bool bNext = !( event.GetId() == idSearchFindPrevious );
  m_findReplace.FindNext( bNext, nullptr, nullptr, false );
}

void MainFrame::OnSearchFindNextSelected( wxCommandEvent& event ) {
  bool bNext = !( event.GetId() == idSearchFindSelectedPrevious );
  m_findReplace.FindSelectedText( bNext );
}

void MainFrame::OnSearchReplace( wxCommandEvent& event ) {
  bool bDoMultipleFiles = ( event.GetId() == idSearchReplaceInFiles );
  if( !bDoMultipleFiles ) {
    bDoMultipleFiles = !Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  }
  m_findReplace.ShowFindDialog( true, bDoMultipleFiles );
}

void MainFrame::OnSearchGotoLine( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  int max = ed->GetControl()->LineFromPosition( ed->GetControl()->GetLength() ) + 1;
  wxString strLine = cbGetTextFromUser( F( _T( "Line (1 - %d): " ), max ), _T( "Goto line" ), _T( "" ), this );
  long int line = 0;
  strLine.ToLong( &line );
  if( line >= 1 && line <= max ) {
    ed->UnfoldBlockFromLine( line - 1 );
    ed->GotoLine( line - 1 );
  }
}

void MainFrame::OnSearchGotoNextChanged( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GotoNextChanged();
  }
}

void MainFrame::OnSearchGotoPrevChanged( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->GotoPreviousChanged();
  }
}

void MainFrame::OnHelpTips( M_Code_unused wxCommandEvent& event ) {
  ShowTips( true );
}

void MainFrame::OnFileMenuUpdateUI( wxUpdateUIEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    event.Skip();
    return;
  }
  if( !ProjectManager::CanShutdown() || !EditorManager::CanShutdown() ) {
    event.Enable( false );
    return;
  }
  EditorManager *editorManager = Manager::Get()->GetEditorManager();
  EditorBase *ed = ( editorManager ? editorManager->GetActiveEditor() : nullptr );
  const int id = event.GetId();
  if( id == idFileClose || id == idFileCloseAll || id == idFileSaveAs ) {
    event.Enable( ed );
  } else if( id == idFileSave ) {
    event.Enable( ed && ed->GetModified() );
  } else if( id == idFileOpen ) {
    event.Enable( true );
  } else {
    ProjectManager *projectManager = Manager::Get()->GetProjectManager();
    M_Code_Project *project = ( projectManager ? projectManager->GetActiveProject() : nullptr );
    if( project && project->GetCurrentlyCompilingTarget() ) {
      event.Enable( false );
      return;
    }
    if( id == idFileReopenProject ) {
      event.Enable( true );
    } else if( id == idFileCloseProject || id == idFileSaveProjectAs || id == idFileSaveProjectTemplate ) {
      event.Enable( project != nullptr );
    } else if( id == idFileSaveProject ) {
      event.Enable( project && project->GetModified() );
    } else if( id == idFileOpenDefWorkspace || id == idFileSaveWorkspaceAs || id == idFileSaveAll ) {
      event.Enable( true );
    } else {
      const M_Code_Workspace *workspace = Manager::Get()->GetProjectManager()->GetWorkspace();
      if( id == idFileSaveWorkspace ) {
        event.Enable( workspace && workspace->GetModified() );
      } else if( id == idFileCloseWorkspace ) {
        event.Enable( workspace != nullptr );
      } else {
        event.Skip();
      }
    }
  }
}

void MainFrame::OnEditMenuUpdateUI( wxUpdateUIEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    event.Skip();
    return;
  }
  M_Code_Editor* ed = NULL;
  EditorBase* eb = NULL;
  bool hasSel = false;
  bool canUndo = false;
  bool canRedo = false;
  bool canSelAll = false;
  if( Manager::Get()->GetEditorManager() && !Manager::IsAppShuttingDown() ) {
    ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
    eb = Manager::Get()->GetEditorManager()->GetActiveEditor();
  }
  wxMenuBar* mbar = GetMenuBar();
  if( eb ) {
    canUndo = eb->CanUndo();
    canRedo = eb->CanRedo();
    hasSel = eb->HasSelection();
    canSelAll = eb->CanSelectAll();
  }
  mbar->Enable( idEditUndo, canUndo );
  mbar->Enable( idEditRedo, canRedo );
  mbar->Enable( idEditClearHistory, canUndo || canRedo );
  mbar->Enable( idEditSwapHeaderSource, ed );
  mbar->Enable( idEditGotoMatchingBrace, ed );
  mbar->Enable( idEditHighlightMode, ed );
  mbar->Enable( idEditSelectAll, canSelAll );
  mbar->Enable( idEditSelectNext, ed );
  mbar->Enable( idEditSelectNextSkip, hasSel );
  mbar->Enable( idEditBookmarks, ed );
  mbar->Enable( idEditFolding, ed && Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/folding/show_folds" ), false ) );
  mbar->Enable( idEditEncoding, ed );
  mbar->Enable( idEditSpecialCommands, ed );
  mbar->Enable( idEditSpecialCommandsCase, ed && hasSel );
  mbar->Enable( idEditCommentSelected, ed );
  mbar->Enable( idEditUncommentSelected, ed );
  mbar->Enable( idEditToggleCommentSelected, ed );
  mbar->Enable( idEditStreamCommentSelected, ed );
  mbar->Enable( idEditBoxCommentSelected, ed );
  mbar->Enable( idEditShowCallTip, ed );
  mbar->Enable( idEditCompleteCode, ed );
  if( ed ) {
    bool defenc = ed && ( ed->GetEncoding() == wxFONTENCODING_SYSTEM || ed->GetEncoding() == wxLocale::GetSystemEncoding() );
    mbar->Check( idEditEncodingDefault, defenc );
    mbar->Check( idEditEncodingUseBom, ed && ed->GetUseBom() );
    mbar->Check( idEditEncodingAscii, ed && ed->GetEncoding() == wxFONTENCODING_ISO8859_1 );
    mbar->Check( idEditEncodingUtf7, ed && ed->GetEncoding() == wxFONTENCODING_UTF7 );
    mbar->Check( idEditEncodingUtf8, ed && ed->GetEncoding() == wxFONTENCODING_UTF8 );
    mbar->Check( idEditEncodingUnicode, ed && ed->GetEncoding() == wxFONTENCODING_UNICODE );
    mbar->Check( idEditEncodingUtf16, ed && ed->GetEncoding() == wxFONTENCODING_UTF16 );
    mbar->Check( idEditEncodingUtf32, ed && ed->GetEncoding() == wxFONTENCODING_UTF32 );
    mbar->Check( idEditEncodingUnicode16BE, ed && ed->GetEncoding() == wxFONTENCODING_UTF16BE );
    mbar->Check( idEditEncodingUnicode16LE, ed && ed->GetEncoding() == wxFONTENCODING_UTF16LE );
    mbar->Check( idEditEncodingUnicode32BE, ed && ed->GetEncoding() == wxFONTENCODING_UTF32BE );
    mbar->Check( idEditEncodingUnicode32LE, ed && ed->GetEncoding() == wxFONTENCODING_UTF32LE );
    wxMenu* hl = nullptr;
    mbar->FindItem( idEditHighlightModeText, &hl );
    if( hl ) {
      EditorColourSet* colour_set = ed->GetColourSet();
      if( colour_set ) {
        mbar->Check( hl->FindItem( colour_set->GetLanguageName( ed->GetLanguage() ) ), true );
      }
    }
  }
  if( m_pToolbar ) {
    m_pToolbar->EnableTool( idEditUndo, canUndo );
    m_pToolbar->EnableTool( idEditRedo, canRedo );
  }
  event.Skip();
}

void MainFrame::OnViewMenuUpdateUI( wxUpdateUIEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    event.Skip();
    return;
  }
  wxMenuBar* mbar = GetMenuBar();
  M_Code_Editor* ed = Manager::Get()->GetEditorManager() ? Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor() : nullptr;
  bool manVis = m_LayoutManager.GetPane( m_pPrjManUI->GetNotebook() ).IsShown();
  mbar->Check( idViewManager, manVis );
  mbar->Check( idViewLogManager, m_LayoutManager.GetPane( m_pInfoPane ).IsShown() );
  mbar->Check( idViewStatusbar, GetStatusBar() && GetStatusBar()->IsShown() );
  mbar->Check( idViewScriptConsole, m_LayoutManager.GetPane( m_pScriptConsole ).IsShown() );
  mbar->Check( idViewHideEditorTabs, Manager::Get()->GetEditorManager()->GetNotebook()->GetTabCtrlHeight() == 0 );
  mbar->Check( idViewFullScreen, IsFullScreen() );
  mbar->Enable( idViewFocusEditor, ed );
  mbar->Enable( idViewFocusManagement, manVis );
  mbar->Enable( idViewFocusLogsAndOthers, m_pInfoPane->IsShown() );
  wxMenu* viewToolbars = nullptr;
  GetMenuBar()->FindItem( idViewToolMain, &viewToolbars );
  if( viewToolbars ) {
    SetChecksForViewToolbarsMenu( *viewToolbars );
  }
  event.Skip();
}

void MainFrame::OnSearchMenuUpdateUI( wxUpdateUIEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    event.Skip();
    return;
  }
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()
                      ? Manager::Get()->GetEditorManager()->GetBuiltinEditor(
                        Manager::Get()->GetEditorManager()->GetActiveEditor() ) : nullptr;
  bool enableGoto = false;
  if( ed )
    enableGoto = Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/margin/use_changebar" ), true )
                 && ( ed->CanUndo() || ed->CanRedo() );
  wxMenuBar* mbar = GetMenuBar();
  mbar->Enable( idSearchFind, ed );
  mbar->Enable( idSearchFindNext, ed );
  mbar->Enable( idSearchFindPrevious, ed );
  mbar->Enable( idSearchFindSelectedNext, ed );
  mbar->Enable( idSearchFindSelectedPrevious, ed );
  mbar->Enable( idSearchReplace, ed );
  mbar->Enable( idSearchGotoLine, ed );
  mbar->Enable( idSearchGotoNextChanged, enableGoto );
  mbar->Enable( idSearchGotoPreviousChanged, enableGoto );
  event.Skip();
}


void MainFrame::OnEditorUpdateUI( CodeBlocksEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    event.Skip();
    return;
  }
  if( Manager::Get()->GetEditorManager() && event.GetEditor() == Manager::Get()->GetEditorManager()->GetActiveEditor() ) {
    DoUpdateStatusBar();
  }
  event.Skip();
}

namespace {
  struct ToolbarFitInfo {
    int row;
    wxRect rect;
    wxWindow *window;

    bool operator<( const ToolbarFitInfo &r ) const {
      if( row < r.row ) {
        return true;
      } else if( row == r.row ) {
        return rect.x < r.rect.x;
      } else
      { return false; }
    }
  };

  static void CollectToolbars( std::set<ToolbarFitInfo> &result, wxAuiManager &layoutManager ) {
    const wxAuiPaneInfoArray &panes = layoutManager.GetAllPanes();
    for( size_t ii = 0; ii < panes.GetCount(); ++ii ) {
      const wxAuiPaneInfo &info = panes[ii];
      if( info.IsToolbar() && info.IsShown() ) {
        ToolbarFitInfo f;
        f.row = info.dock_row;
        f.rect = info.rect;
        f.window = info.window;
        result.insert( f );
      }
    }
  }

  struct ToolbarRowInfo {
    ToolbarRowInfo() {}
    ToolbarRowInfo( int width_, int position_ ) : width( width_ ), position( position_ ) {}

    int width, position;
  };


  static void FitToolbars( wxAuiManager &layoutManager, wxWindow *mainFrame ) {
    std::set<ToolbarFitInfo> sorted;
    CollectToolbars( sorted, layoutManager );
    if( sorted.empty() ) {
      return;
    }
    int maxWidth = mainFrame->GetSize().x;
    int gripperSize = layoutManager.GetArtProvider()->GetMetric( wxAUI_DOCKART_GRIPPER_SIZE );
    std::vector<ToolbarRowInfo> rows;
    std::vector<wxWindow*> nonFitingToolbars;
    for( std::set<ToolbarFitInfo>::const_iterator it = sorted.begin(); it != sorted.end(); ++it ) {
      wxAuiPaneInfo &pane = layoutManager.GetPane( it->window );
      int row = pane.dock_row;
      while( static_cast<int>( rows.size() ) <= row ) {
        rows.push_back( ToolbarRowInfo( 0, 0 ) );
      }
      int maxX = rows[row].width + it->window->GetBestSize().x + gripperSize;
      if( maxX > maxWidth ) {
        nonFitingToolbars.push_back( it->window );
      } else {
        rows[row].width = maxX;
        pane.Position( rows[row].position++ );
      }
    }
    int lastRow = rows.empty() ? 0 : ( rows.size() - 1 );
    int position = rows.back().position, maxX = rows.back().width;
    for( std::vector<wxWindow*>::iterator it = nonFitingToolbars.begin(); it != nonFitingToolbars.end(); ++it ) {
      maxX += ( *it )->GetBestSize().x;
      maxX += gripperSize;
      if( maxX > maxWidth ) {
        position = 0;
        lastRow++;
        maxX = ( *it )->GetBestSize().x + gripperSize;
      }
      layoutManager.GetPane( *it ).Position( position++ ).Row( lastRow );
    }
  }

  static void OptimizeToolbars( wxAuiManager &layoutManager, wxWindow *mainFrame ) {
    std::set<ToolbarFitInfo> sorted;
    CollectToolbars( sorted, layoutManager );
    if( sorted.empty() ) {
      return;
    }
    int maxWidth = mainFrame->GetSize().x;
    int lastRow = 0, position = 0, maxX = 0;
    int gripperSize = layoutManager.GetArtProvider()->GetMetric( wxAUI_DOCKART_GRIPPER_SIZE );
    for( std::set<ToolbarFitInfo>::const_iterator it = sorted.begin(); it != sorted.end(); ++it ) {
      maxX += it->window->GetBestSize().x;
      maxX += gripperSize;
      if( maxX > maxWidth ) {
        position = 0;
        lastRow++;
        maxX = it->window->GetBestSize().x + gripperSize;
      }
      layoutManager.GetPane( it->window ).Position( position++ ).Row( lastRow );
    }
  }

}

void MainFrame::OnViewToolbarsFit( M_Code_unused wxCommandEvent& event ) {
  FitToolbars( m_LayoutManager, this );
  DoUpdateLayout();
}

void MainFrame::OnViewToolbarsOptimize( M_Code_unused wxCommandEvent& event ) {
  OptimizeToolbars( m_LayoutManager, this );
  DoUpdateLayout();
}

void MainFrame::OnToggleBar( wxCommandEvent& event ) {
  wxWindow* win = nullptr;
  bool toolbar = false;
  if( event.GetId() == idViewManager ) {
    win = m_pPrjManUI->GetNotebook();
  } else if( event.GetId() == idViewLogManager ) {
    win = m_pInfoPane;
  } else if( event.GetId() == idViewToolMain ) {
    win = m_pToolbar;
    toolbar = true;
  } else if( event.GetId() == idViewToolDebugger ) {
    win = m_debuggerToolbarHandler->GetToolbar();
    toolbar = true;
  } else {
    wxString pluginName = m_PluginIDsMap[event.GetId()];
    if( !pluginName.IsEmpty() ) {
      M_Code_Plugin* plugin = Manager::Get()->GetPluginManager()->FindPluginByName( pluginName );
      if( plugin ) {
        win = m_PluginsTools[plugin];
        toolbar = true;
      }
    }
  }
  if( win ) {
    if( !event.IsChecked() ) {
      m_LayoutManager.GetPane( win ).BestSize( win->GetSize() );
    }
    m_LayoutManager.GetPane( win ).Show( event.IsChecked() );
    if( toolbar ) {
      FitToolbars( m_LayoutManager, this );
    }
    DoUpdateLayout();
  }
}

void MainFrame::OnToggleStatusBar( M_Code_unused wxCommandEvent& event ) {
  M_Code_StatusBar* sb = ( M_Code_StatusBar* )GetStatusBar();
  if( !sb ) {
    return;
  }
  ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "app" ) );
  const bool show = !cfg->ReadBool( _T( "/main_frame/statusbar" ), true );
  cfg->Write( _T( "/main_frame/statusbar" ), show );
  DoUpdateStatusBar();
  sb->Show( show );
  if( show ) {
    SendSizeEvent();
  }
  DoUpdateLayout();
}

void MainFrame::OnFocusEditor( M_Code_unused wxCommandEvent& event ) {
  EditorManager* edman = Manager::Get()->GetEditorManager();
  M_Code_AuiNotebook* nb = edman ? edman->GetNotebook() : nullptr;
  if( nb ) {
    nb->FocusActiveTabCtrl();
  }
}

void MainFrame::OnFocusManagement( M_Code_unused wxCommandEvent& event ) {
  M_Code_AuiNotebook* nb = m_pPrjManUI ? m_pPrjManUI->GetNotebook() : nullptr;
  if( nb ) {
    nb->FocusActiveTabCtrl();
  }
}

void MainFrame::OnFocusLogsAndOthers( M_Code_unused wxCommandEvent& event ) {
  if( m_pInfoPane ) {
    m_pInfoPane->FocusActiveTabCtrl();
  }
}

void MainFrame::OnSwitchTabs( M_Code_unused wxCommandEvent& event ) {
  M_Code_AuiNotebook* nb = Manager::Get()->GetEditorManager()->GetNotebook();
  if( !nb ) {
    return;
  }
  wxSwitcherItems items;
  items.AddGroup( _T( "打开文件" ), _T( "editors" ) );
  if( !Manager::Get()->GetConfigManager( _T( "app" ) )->ReadBool( _T( "/environment/tabs_stacked_based_switching" ) ) ) {
    for( size_t i = 0; i < nb->GetPageCount(); ++i ) {
      wxString title = nb->GetPageText( i );
      wxWindow* window = nb->GetPage( i );
      items.AddItem( title, title, GetEditorDescription( static_cast<EditorBase*>( window ) ), i, nb->GetPageBitmap( i ) ).SetWindow( window );
    }
    int idx = items.GetIndexForFocus();
    if( idx != wxNOT_FOUND ) {
      items.SetSelection( idx );
    }
  } else {
    int index = 0;
    cbNotebookStack* body;
    for( body = Manager::Get()->GetEditorManager()->GetNotebookStack(); body != NULL; body = body->next ) {
      index = nb->GetPageIndex( body->window );
      if( index == wxNOT_FOUND ) {
        continue;
      }
      wxString title = nb->GetPageText( index );
      items.AddItem( title, title, GetEditorDescription( static_cast<EditorBase*>( body->window ) ), index, nb->GetPageBitmap( index ) ).SetWindow( body->window );
    }
    if( items.GetItemCount() > 2 ) {
      items.SetSelection( 2 );
    } else
    { items.SetSelection( items.GetItemCount() - 1 ); }
  }
  wxSwitcherDialog dlg( items, wxGetApp().GetTopWindow() );
  dlg.SetExtraNavigationKey( _T( ',' ) );
  int answer = dlg.ShowModal();
  if( ( answer == wxID_OK ) && ( dlg.GetSelection() != -1 ) ) {
    wxSwitcherItem& item = items.GetItem( dlg.GetSelection() );
    wxWindow* win = item.GetWindow();
    if( win ) {
      nb->SetSelection( item.GetId() );
      win->SetFocus();
    }
  }
}

void MainFrame::OnToggleFullScreen( M_Code_unused wxCommandEvent& event ) {
  ShowFullScreen( !IsFullScreen(), wxFULLSCREEN_NOTOOLBAR | wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION );
  if( IsFullScreen() ) {
    wxSize containerSize = GetClientSize();
    wxSize buttonSize = m_pCloseFullScreenBtn->GetSize();
    m_pCloseFullScreenBtn->Move( containerSize.GetWidth() - buttonSize.GetWidth(),
                                 containerSize.GetHeight() - buttonSize.GetHeight() );
    m_pCloseFullScreenBtn->Show( true );
    m_pCloseFullScreenBtn->Raise();
  } else {
    m_pCloseFullScreenBtn->Show( false );
  }
}

void MainFrame::OnPluginInstalled( CodeBlocksEvent& event ) {
  PluginsUpdated( event.GetPlugin(), Installed );
}

void MainFrame::OnPluginUninstalled( CodeBlocksEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    return;
  }
  PluginsUpdated( event.GetPlugin(), Uninstalled );
}

void MainFrame::OnPluginLoaded( CodeBlocksEvent& event ) {
  M_Code_Plugin* plug = event.GetPlugin();
  if( plug ) {
    DoAddPlugin( plug );
    const PluginInfo* info = Manager::Get()->GetPluginManager()->GetPluginInfo( plug );
    wxString msg = info ? info->title : wxString( _T( "<Unknown plugin>" ) );
    Manager::Get()->GetLogManager()->DebugLog( F( _T( "%s plugin activated" ), msg.wx_str() ) );
  }
}

void MainFrame::OnPluginUnloaded( CodeBlocksEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    return;
  }
  M_Code_Plugin* plugin = event.GetPlugin();
  M_Code_StatusBar *sb = ( M_Code_StatusBar* )GetStatusBar();
  if( sb ) {
    sb->RemoveField( plugin );
  }
  if( m_PluginsTools[plugin] ) {
    bool result = m_PluginsTools[plugin]->Disconnect( wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED, wxCommandEventHandler( MainFrame::OnToolBarRightClick ) );
    cbAssert( result );
    m_LayoutManager.DetachPane( m_PluginsTools[plugin] );
    m_PluginsTools[plugin]->Destroy();
    m_PluginsTools.erase( plugin );
    DoUpdateLayout();
  }
  PluginsUpdated( plugin, Unloaded );
}

void MainFrame::OnSettingsEnvironment( M_Code_unused wxCommandEvent& event ) {
  bool tbarsmall = m_SmallToolBar;
  bool needRestart = false;
  EnvironmentSettingsDlg dlg( this, m_LayoutManager.GetArtProvider() );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    DoUpdateEditorStyle();
    DoUpdateLayoutColours();
    m_SmallToolBar = true;
    needRestart = m_SmallToolBar != tbarsmall;
    Manager::Get()->GetLogManager()->NotifyUpdate();
    Manager::Get()->GetEditorManager()->RecreateOpenEditorStyles();
    Manager::Get()->GetCCManager()->UpdateEnvSettings();
    m_pPrjManUI->RebuildTree();
    CodeBlocksEvent event2( cbEVT_SETTINGS_CHANGED );
    event2.SetInt( cbSettingsType::Environment );
    Manager::Get()->ProcessEvent( event2 );
  }
  if( needRestart ) {
    InfoWindow::Tip( _T( "需要重新启动才能使更改生效." ), _T( "提示" ), 2500 );
  }
}

void MainFrame::OnGlobalUserVars( M_Code_unused wxCommandEvent& event ) {
  Manager::Get()->GetUserVariableManager()->Configure();
}

void MainFrame::OnSettingsEditor( M_Code_unused wxCommandEvent& event ) {
  wxBeginBusyCursor();
  EditorConfigurationDlg dlg( Manager::Get()->GetAppWindow() );
  PlaceWindow( &dlg );
  wxEndBusyCursor();
  if( dlg.ShowModal() == wxID_OK ) {
    Manager::Get()->GetEditorManager()->RecreateOpenEditorStyles();
    CodeBlocksEvent event2( cbEVT_SETTINGS_CHANGED );
    event2.SetInt( cbSettingsType::Editor );
    Manager::Get()->ProcessEvent( event2 );
  }
}

void MainFrame::OnSettingsCompiler( M_Code_unused wxCommandEvent& event ) {
  CompilerSettingsDlg dlg( this );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    CodeBlocksEvent event2( cbEVT_SETTINGS_CHANGED );
    event2.SetInt( cbSettingsType::Compiler );
    Manager::Get()->ProcessEvent( event2 );
  }
}

void MainFrame::OnSettingsDebugger( M_Code_unused wxCommandEvent& event ) {
  DebuggerSettingsDlg dlg( this );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    CodeBlocksEvent event2( cbEVT_SETTINGS_CHANGED );
    event2.SetInt( cbSettingsType::Debugger );
    Manager::Get()->ProcessEvent( event2 );
  }
}

void MainFrame::OnSettingsPlugins( M_Code_unused wxCommandEvent& event ) {
  if( Manager::Get()->GetPluginManager()->Configure() == wxID_OK ) {
    CodeBlocksEvent event2( cbEVT_SETTINGS_CHANGED );
    event2.SetInt( cbSettingsType::Plugins );
    Manager::Get()->ProcessEvent( event2 );
  }
}

void MainFrame::OnSettingsScripting( M_Code_unused wxCommandEvent& event ) {
  ScriptingSettingsDlg dlg( this );
  if( dlg.ShowModal() == wxID_OK ) {
    RunStartupScripts();
  }
}

void MainFrame::OnProjectActivated( CodeBlocksEvent& event ) {
  DoUpdateAppTitle();
  event.Skip();
}

void MainFrame::OnProjectOpened( CodeBlocksEvent& event ) {
  event.Skip();
}

void MainFrame::OnEditorOpened( CodeBlocksEvent& event ) {
  DoUpdateAppTitle();
  event.Skip();
}

void MainFrame::OnEditorActivated( CodeBlocksEvent& event ) {
  DoUpdateAppTitle();
  DoUpdateStatusBar();
  EditorBase *editor = event.GetEditor();
  if( editor && editor->IsBuiltinEditor() ) {
    ConfigManager* cfgEditor = Manager::Get()->GetConfigManager( _T( "editor" ) );
    if( cfgEditor->ReadBool( _T( "/sync_editor_with_project_manager" ), false ) ) {
      ProjectFile* pf = static_cast<M_Code_Editor*>( editor )->GetProjectFile();
      if( pf ) {
        m_pPrjManUI->ShowFileInTree( *pf );
      }
    }
  }
  event.Skip();
}

void MainFrame::OnEditorClosed( CodeBlocksEvent& event ) {
  DoUpdateAppTitle();
  DoUpdateStatusBar();
  event.Skip();
}

void MainFrame::OnEditorSaved( CodeBlocksEvent& event ) {
  DoUpdateAppTitle();
  event.Skip();
}

void MainFrame::OnEditorModified( CodeBlocksEvent& event ) {
  DoUpdateAppTitle();
  event.Skip();
}

void MainFrame::OnProjectClosed( CodeBlocksEvent& event ) {
  event.Skip();
}

void MainFrame::OnPageChanged( wxNotebookEvent& event ) {
  DoUpdateAppTitle();
  event.Skip();
}

void MainFrame::OnShiftTab( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->DoUnIndent();
  }
}

void MainFrame::OnCtrlAltTab( M_Code_unused wxCommandEvent& event ) {
  wxCommandEvent dummy;
  switch( m_LastCtrlAltTabWindow ) {
    case 1:
      m_LastCtrlAltTabWindow = 2;
      OnFocusEditor( dummy );
      break;
    case 2:
      m_LastCtrlAltTabWindow = 3;
      OnFocusLogsAndOthers( dummy );
      break;
    case 3:
    default:
      m_LastCtrlAltTabWindow = 1;
      OnFocusManagement( dummy );
  }
}

void MainFrame::OnRequestDockWindow( CodeBlocksDockEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    return;
  }
  wxAuiPaneInfo info;
  wxString name = event.name;
  if( name.IsEmpty() ) {
    static int idx = 0;
    name = F( _T( "UntitledPane%d" ), ++idx );
  }
  info.Name( name );
  info.Caption( event.title.IsEmpty() ? name : event.title );
  switch( event.dockSide ) {
    case CodeBlocksDockEvent::dsLeft:
      info.Left();
      break;
    case CodeBlocksDockEvent::dsRight:
      info.Right();
      break;
    case CodeBlocksDockEvent::dsTop:
      info.Top();
      break;
    case CodeBlocksDockEvent::dsBottom:
      info.Bottom();
      break;
    case CodeBlocksDockEvent::dsFloating:
      info.Float();
      break;
    case CodeBlocksDockEvent::dsUndefined:
    default:
      break;
  }
  info.Show( event.shown );
  info.BestSize( event.desiredSize );
  info.FloatingSize( event.floatingSize );
  info.FloatingPosition( event.floatingPos );
  info.MinSize( event.minimumSize );
  info.Layer( event.stretch ? 1 : 0 );
  if( event.row != -1 ) {
    info.Row( event.row );
  }
  if( event.column != -1 ) {
    info.Position( event.column );
  }
  info.CloseButton( event.hideable ? true : false );
  m_LayoutManager.AddPane( event.pWindow, info );
  DoUpdateLayout();
}

void MainFrame::OnRequestUndockWindow( CodeBlocksDockEvent& event ) {
  wxAuiPaneInfo info = m_LayoutManager.GetPane( event.pWindow );
  if( info.IsOk() ) {
    m_LayoutManager.DetachPane( event.pWindow );
    DoUpdateLayout();
  }
}

void MainFrame::OnRequestShowDockWindow( CodeBlocksDockEvent& event ) {
  m_LayoutManager.GetPane( event.pWindow ).Show();
  DoUpdateLayout();
  CodeBlocksDockEvent evt( cbEVT_DOCK_WINDOW_VISIBILITY );
  evt.pWindow = event.pWindow;
  Manager::Get()->ProcessEvent( evt );
}

void MainFrame::OnRequestHideDockWindow( CodeBlocksDockEvent& event ) {
  m_LayoutManager.GetPane( event.pWindow ).Hide();
  DoUpdateLayout();
  CodeBlocksDockEvent evt( cbEVT_DOCK_WINDOW_VISIBILITY );
  evt.pWindow = event.pWindow;
  Manager::Get()->ProcessEvent( evt );
}

void MainFrame::OnDockWindowVisibility( M_Code_unused CodeBlocksDockEvent& event ) {
}

void MainFrame::OnLayoutUpdate( M_Code_unused CodeBlocksLayoutEvent& event ) {
  DoFixToolbarsLayout();
  DoUpdateLayout();
}

void MainFrame::OnLayoutQuery( CodeBlocksLayoutEvent& event ) {
  event.layout = !m_LastLayoutName.IsEmpty() ? m_LastLayoutName : gDefaultLayout;
  event.StopPropagation();
}

void MainFrame::OnLayoutSwitch( CodeBlocksLayoutEvent& event ) {
  LoadViewLayout( event.layout, true );
}

void MainFrame::OnAddLogWindow( CodeBlocksLogEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    return;
  }
  wxWindow* p = event.window;
  if( p ) {
    m_pInfoPane->AddNonLogger( p, event.title, event.icon );
  } else {
    p = event.logger->CreateControl( m_pInfoPane );
    if( p ) {
      m_pInfoPane->AddLogger( event.logger, p, event.title, event.icon );
    }
  }
  Manager::Get()->GetLogManager()->NotifyUpdate();
}

void MainFrame::OnRemoveLogWindow( CodeBlocksLogEvent& event ) {
  if( Manager::IsAppShuttingDown() ) {
    return;
  }
  if( event.window ) {
    m_pInfoPane->RemoveNonLogger( event.window );
  } else {
    m_pInfoPane->DeleteLogger( event.logger );
  }
}

void MainFrame::OnHideLogWindow( CodeBlocksLogEvent& event ) {
  if( event.window ) {
    m_pInfoPane->HideNonLogger( event.window );
  } else if( event.logger ) {
    m_pInfoPane->Hide( event.logger );
  }
}

void MainFrame::OnSwitchToLogWindow( CodeBlocksLogEvent& event ) {
  if( event.window ) {
    m_pInfoPane->ShowNonLogger( event.window );
  } else if( event.logger ) {
    m_pInfoPane->Show( event.logger );
  }
}

void MainFrame::OnGetActiveLogWindow( CodeBlocksLogEvent& event ) {
  bool is_logger;
  int page_index = m_pInfoPane->GetCurrentPage( is_logger );
  event.logger = nullptr;
  event.window = nullptr;
  if( is_logger ) {
    event.logger = m_pInfoPane->GetLogger( page_index );
  } else {
    event.window = m_pInfoPane->GetWindow( page_index );
  }
}

void MainFrame::OnShowLogManager( M_Code_unused CodeBlocksLogEvent& event ) {
  if( !Manager::Get()->GetConfigManager( _T( "message_manager" ) )->ReadBool( _T( "/auto_hide" ), false ) ) {
    return;
  }
  m_LayoutManager.GetPane( m_pInfoPane ).Show( true );
  DoUpdateLayout();
}

void MainFrame::OnHideLogManager( M_Code_unused CodeBlocksLogEvent& event ) {
  if( !Manager::Get()->GetConfigManager( _T( "message_manager" ) )->ReadBool( _T( "/auto_hide" ), false ) ||
      m_AutoHideLockCounter > 0 ) {
    return;
  }
  m_LayoutManager.GetPane( m_pInfoPane ).Show( false );
  DoUpdateLayout();
}

void MainFrame::OnLockLogManager( M_Code_unused CodeBlocksLogEvent& event ) {
  if( !Manager::Get()->GetConfigManager( _T( "message_manager" ) )->ReadBool( _T( "/auto_hide" ), false ) ) {
    return;
  }
  ++m_AutoHideLockCounter;
}

void MainFrame::OnUnlockLogManager( M_Code_unused CodeBlocksLogEvent& event ) {
  if( !Manager::Get()->GetConfigManager( _T( "message_manager" ) )->ReadBool( _T( "/auto_hide" ), false ) &&
      m_AutoHideLockCounter > 0 ) {
    return;
  }
  if( --m_AutoHideLockCounter == 0 ) {
    m_LayoutManager.GetPane( m_pInfoPane ).Show( false );
    DoUpdateLayout();
  }
}

void MainFrame::OnHighlightMenu( M_Code_unused wxCommandEvent& event ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( !ed ) {
    return;
  }
  EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
  if( !colour_set ) {
    return;
  }
  wxMenu* hl = nullptr;
  GetMenuBar()->FindItem( idEditHighlightModeText, &hl );
  wxArrayString langs = colour_set->GetAllHighlightLanguages();
  wxMenu mm;
  mm.AppendRadioItem( idEditHighlightModeText, _T( "Plain text" ), _T( "将当前文档的突出显示模式切换为 \"纯文本\"" ) );
  Connect( hl->FindItem( _T( "Plain text" ) ), -1, wxEVT_COMMAND_MENU_SELECTED,
           ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
           &MainFrame::OnEditHighlightMode );
  for( size_t i = 0; i < langs.GetCount(); ++i ) {
    if( i > 0 && !( i % 20 ) ) {
      mm.Break();
    }
    mm.AppendRadioItem( hl->FindItem( langs[i] ), langs[i],
                        F( _T( "将当前文档的突出显示模式切换为 \"%s\"" ), langs[i].wx_str() ) );
    Connect( hl->FindItem( langs[i] ), -1, wxEVT_COMMAND_MENU_SELECTED,
             ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
             &MainFrame::OnEditHighlightMode );
  }
  int checkeditem = -1;
  checkeditem = hl->FindItem( colour_set->GetLanguageName( ed->GetLanguage() ) );
  if( checkeditem != wxNOT_FOUND ) {
    mm.Check( checkeditem, true );
  }
  wxRect rect;
  GetStatusBar()->GetFieldRect( 1, rect );
  PopupMenu( &mm, GetStatusBar()->GetPosition() + rect.GetPosition() );
}

void MainFrame::StartupDone() {
  m_StartupDone = true;
  DoUpdateLayout();
}

wxStatusBar* MainFrame::OnCreateStatusBar( int number, long style, wxWindowID id, const wxString& name ) {
  M_Code_StatusBar* sb = new M_Code_StatusBar( this, id, style, name );
  sb->SetFieldsCount( number );
  return sb;
}

void MainFrame::OnMouseRightUp( wxMouseEvent& event ) {
  PopupToggleToolbarMenu();
  event.Skip();
}

void MainFrame::OnToolBarRightClick( wxCommandEvent& event ) {
  PopupToggleToolbarMenu();
  event.Skip();
}

void MainFrame::PopupToggleToolbarMenu() {
  wxMenuBar* menuBar = Manager::Get()->GetAppFrame()->GetMenuBar();
  int idx = menuBar->FindMenu( _T( "视图(&V)" ) );
  if( idx == wxNOT_FOUND ) {
    return;
  }
  wxMenu* viewMenu = menuBar->GetMenu( idx );
  idx = viewMenu->FindItem( _T( "工具栏" ) );
  if( idx == wxNOT_FOUND ) {
    return;
  }
  wxMenu* toolbarMenu = viewMenu->FindItem( idx )->GetSubMenu();
  wxMenu menu;
  for( size_t ii = 0; ii < toolbarMenu->GetMenuItemCount(); ++ii ) {
    wxMenuItem *old = toolbarMenu->FindItemByPosition( ii );
    if( !old ) {
      continue;
    }
    wxMenuItem *item;
    item = new wxMenuItem( &menu, old->GetId(), old->GetItemLabelText(), old->GetHelp(), old->GetKind() );
    menu.Append( item );
  }
  SetChecksForViewToolbarsMenu( menu );
  PopupMenu( &menu );
}

void MainFrame::SetChecksForViewToolbarsMenu( wxMenu &menu ) {
  for( size_t i = 0; i < menu.GetMenuItemCount(); ++i ) {
    wxMenuItem* item = menu.GetMenuItems()[i];
    wxString pluginName = m_PluginIDsMap[item->GetId()];
    if( !pluginName.IsEmpty() ) {
      M_Code_Plugin* plugin = Manager::Get()->GetPluginManager()->FindPluginByName( pluginName );
      if( plugin ) {
        item->Check( m_LayoutManager.GetPane( m_PluginsTools[plugin] ).IsShown() );
      }
    }
  }
  menu.Check( idViewToolMain, m_LayoutManager.GetPane( m_pToolbar ).IsShown() );
  menu.Check( idViewToolDebugger, m_LayoutManager.GetPane( m_debuggerToolbarHandler->GetToolbar( false ) ).IsShown() );
}
